困っていたこと
Next.jsで管理画面などの実装をする際にはbeforeunload使いたいときありますよね〜
ただNext.jsの<Link>
タグやRouter.push()
でページ遷移をしてもbeforeunloadイベントは発行されません。
(SPAではフルページリロードではなくView間でのみページ遷移を行っている為当たり前)
参照: Browser Back Button does not trigger window.onbeforeunload · Issue #2694 · vercel/next.js
ここで地味に詰まっていた。。。
解決策
Next.jsの<Link>
タグやRouter.push()
の代わりに、<a>
タグやwindow.location.hrefを使う。
これが現状の解決策になりそうです。
(SPAとしてのページ遷移は諦める)
ちなみに
実際に書くとこんな感じになります。
import React, { useEffect } from 'react';
import { Button } from 'antd';
// 共通の関数をイベント登録、解除時に渡す必要あり
const showAlert = (event: any) => {
event.preventDefault();
event.returnValue = '';
};
type Props = {
handleClick: (event: any) => void;
};
const Main: React.FC<Props> = () => {
const handleRemoveClick = () =>
// ページ離脱前の確認(イベント解除)
window.removeEventListener('beforeunload', showAlert, false);
const handleLinkBtnClick = () => {
window.location.href = '/';
};
return (
<>
<Button type="primary" htmlType="submit" onClick={handleRemoveClick}>
イベント解除
</Button>
<a href="/">リンク1</a>
<Button type="primary" htmlType="submit" onClick={handleLinkBtnClick}>
リンク2
</Button>
</>
);
};
const Index = () => {
useEffect(() => {
// ページ離脱前の確認(イベント登録)
if (window) window.addEventListener('beforeunload', showAlert, false);
}, []);
return (
<>
<h1>beforeUnload</h1>
<Main handleClick={showAlert} />
</>
);
};
export default Index;
return (
<Button type="primary" htmlType="submit" onClick={handleClick}>
イベント解除
</Button>
);
};
const Index = () => {
useEffect(() => {
// ページ離脱前の確認(イベント登録)
if (window) window.addEventListener('beforeunload', showAlert, false);
}, []);
return (
<>
<h1>beforeUnload</h1>
<Main handleClick={showAlert} />
</>
);
};
export default Index;
最後に
以上、「Next.jsでbeforeunloadイベントを扱う」でした。
Next.js有能だしとりあえずnext/routerや../link使っときゃいいっしょ〜的な甘い考えに潜む落とし穴でした。
これが最善の方法かはわかりませんが行き詰まった方はお試しください。
もっといい方法があればコメント待ってます!!