こんにちわ。にっぱちです。
現在業務で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も初挑戦なのでこれからもがんばりたいです。