🙄
Next.JSでページ遷移する前にbeforeunloadはさみたい時
フォームに入力中のデータがの残っていて、ページ遷移する前に注意(window.confirm()
)を出したくて、色々ハマりましたので、書き残しておきます。
結論から言う、beforeunload
はwindow.location.href
やF5
ようなページの再読み込むが発生する時には発火しますが、Next.jsのRouter
のような ブラウザーのhistory API
での画面遷移の場合は発火しないようです。
解決策をいくつか見つかったので、全部書いておきます。
解決策1
import SingletonRouter, { Router } from 'next/router'
useEffect(() => {
SingletonRouter.router.change = (...args) => {
if (confirm('Want to leave?')) {
return Router.prototype.change.apply(SingletonRouter.router, args)
} else {
return new Promise((resolve, reject) => resolve(false))
}
}
return () => {
delete SingletonRouter.router.change
}
}, [])
github issue にありました、https://github.com/vercel/next.js/issues/2476#issuecomment-612483261
router
のchange
というプライベートメソッドを上書きする事でページ遷移をキャンセルできるみたいです、詳しいソースコードは見てませんが、JSはともかくTS使う場合は「プライベートメソッドは上書きできないぞー」とのコンパイルエラーが出ます。
解決策2
import { useRouter } from 'next/router';
const router = useRouter();
const pageChangeHandler = () => {
const answer = window.confirm('コメント内容がリセットされます、本当にページ遷移しますか?');
if(!answer) {
throw 'Abort route';
}
};
useEffect(() => {
router.events.on('routeChangeStart', pageChangeHandler);
return () => {
router.events.off('routeChangeStart', pageChangeHandler)
};
}, []);
同じくgithub issueから発見 https://github.com/vercel/next.js/issues/2476#issuecomment-563190607
ページ遷移する前に発火するrouteChangeStart
にハンドラーを登録しておいて、ハンドラーの中でエラーを発生させて処理を止める。
結論
解決策2の方が安全な気がします、ただ、逆にNext.JS内での遷移制御だけでは、F5
のような画面再読み込みの場合、発火しないので、beforeunload
も必要ですね。
つまり👇なります。
import { useRouter } from 'next/router';
const router = useRouter();
const pageChangeHandler = () => {
const answer = window.confirm('コメント内容がリセットされます、本当にページ遷移しますか?');
if(!answer) {
throw 'Abort route';
}
};
const beforeUnloadhandler = (event) => {
event.returnValue = 'コメント内容がリセットされます、本当にページ遷移しますか?';
};
useEffect(() => {
router.events.on('routeChangeStart', pageChangeHandler);
window.addEventListener('beforeunload', beforeUnloadhandler);
return () => {
router.events.off('routeChangeStart', pageChangeHandler)
window.removeEventListener('beforeunload', beforeUnloadhandler);
};
}, []);
Discussion