こんにちわ。にっぱちです。
現在業務でReactを触っており結構詰まった件について書いていきます。
やりたいこと
あるページだけブラウザバックイベントを感知してコールバックを実行するというものです。
以下のようにページA、ページB、ページCと3つのページがあったとして、ページBからブラウザバックした時だけ処理を行いたい状況です。
const PageA = () => {
  return (
    <h1>page a</h1>
    <Link to="/page-b">PageB</Link>
  )
}
const PageB = () => {
  return (
    <h1>page b</h1>
    <Link to="/page-c">PageC</Link>
  )
}
const PageC = () => {
  return (
    <h1>page c</h1>
  )
}やったこと
まず普通にイベントリスナーを追加してみました。
const PageB = () => {
  const callback = (event) => {
    alert('もどる')
  }
  useEffect(() => {
    window.onpopstate = callback
  })
  return (
    <h1>page b</h1>
    <Link to="/page-c">PageC</Link>
  )
}やりたいことはできましたが、遷移先のページCから戻ってもアラートが表示されました。spaゆえにイベントが生きてる状態なのでしょう。
ページ破棄のタイミングでイベントを削除してみましたが、今度はイベントが発火しなくなりました。
useEffect(() => {
  window.onpopstate = callback
  return () => window.onpopstate = null
})遷移先のページCでイベントを削除してやるとページBのみイベントが発火して今回やりたいことはできました。ただ依存関係は作りたくなく、あくまでページB内で完結したいです。
最終的な形
ページ破棄のタイミングでイベントを削除してみましたが、今度はイベントが発火しなくなりました。
まずこの問題を解決します。最終的にはこうです。
const callback = (event) => {
  alert('もどる')
  history.back()
}
useEffect(() => {
  history.pushState(null, null, null)
  window.onpopstate = callback
  return () => window.onpopstate = null
})history.pushState(null, null, null)でページ遷移なく履歴スタックを追加することができます。さらにコールバック関数内でブラウザバック処理を追加し、これによりページBから戻った時イベントが発火されました。
しかし、ページCから戻り、さらに戻るとページAを通り過ぎさらに前のページが表示されました。
原因としてはページCから戻ってページBが表示されるときに新たに履歴スタックが追加されたことでした。そのためスタックを積み上げる条件を指定しました。
const callback = (event) => {
  alert('もどる')
  history.back()
}
useEffect(() => {
  // ページAから遷移したときだけ履歴スタックを追加する
  if (!('dummy' in history.state)) {
    history.pushState({dummy: ''}, null, null)
  }
  window.onpopstate = callback
  return () => window.onpopstate = null
})これでうまくいきました。
感想
History APIという存在を知り勉強なりました。
また、SPAも初挑戦なのでこれからもがんばりたいです。