[Next.js] stateの値によってはページ移動時に確認を表示する

プログラミング

わたすけです。

Next.jsで、こんな感じのウィンドウを出したいと思ったことは有りませんか?

よく見るやつですね。ページ移動時にこれを表示する方法はちらほら見受けられるのですが、例えば「フォームの内容が更新されている」という状態をstateで管理していて、それをもとに確認ウィンドウの表示・非表示を切り替えるのは少しややこしい方法が必要です。

ということで、今回はやり方を紹介しようと思います。

スポンサーリンク

環境

React 17.0.2 + Next.js 12.0.7、TypeScriptで解説をします。あとFunction Componentでのやり方です(Class Componentでもなんとかなると思います)。

前提

useStateでboolean値を管理します。

const [edited, SetEdited] = React.useState(false);

useEffect等を使って、何かしらの編集が行われたらeditedがtrueになるようにしておきます。

さて、そもそもNext.jsでページ移動時に確認を表示する方法は、以下の2つです。

  • Event Listener
  • Router Events

というか、完全に対応するためにはどちらも使わなければいけません。

まず、EventListenerの方はNextに限らない方法で、

const Confirm = (e: BeforeUnloadEvent): void => {
  e.preventDefault();
  e.returnValue = '変更は破棄されます。ページを移動してもよろしいですか?';
};

window.addEventListener('beforeunload', Confirm);

こんな感じでOKです。ただし、Next.jsの場合、この関数はリロード時のみ呼ばれます。つまり、router.push等の関数でページを移動する場合、何も警告が出ないということになります。

これを防ぐために使うのがRouter Eventsです。

const RouterEvent = () => {
  if (!window.confirm('変更は破棄されます。ページを移動してもよろしいですか?')) {
    throw 'canceled';
  }
};

const router = useRouter();
router.evens.on('routeChangeStart', RouterEvent);

windows.confirmでキャンセルが押されたらthrowによってRouterのイベントを潰している感じです。

問題点・対策

さて、ここで先程の「editedがtrueでなければ表示しない」という要件を満たそうとする時、真っ先に挙がるのは「if (!edited) return; を関数の一番上に書く」でしょう。

ただし。これでは動かないはずです。console.logとかを使えばわかりますが、stateが更新されてもページ移動時には無関係になります(初期値が採用されてるらしい?あんまり詳しく見てないです)

ということで、これを解決するためには、以下の手順が必要です。

useCallbackを使う

関数をuseCallbackで囲います。

const hoge = React.useCallback(() => ~, []);

stateではなくrefを使う

useRefを用いてrefを作成し、ref.currentで参照します。

const edited = React.useRef(false);

ということで、完成形はこのような感じになります。

const Confirm = React.useCallback((e: BeforeUnloadEvent): void => {
  if (!edited.current) return;
  e.preventDefault();
  e.returnValue = '変更は破棄されます。ページを移動してもよろしいですか?';
}, []);

window.addEventListener('beforeunload', Confirm);

カスタムフック化するとなお使いやすいと思います。実装はこんな感じになります。

File not found · watasuke102/TAGether
Share your self-made exam with classmates. Contribute to watasuke102/TAGether development by creating an account on GitHub.

おわりに

ということで、Next.jsで確認を表示する方法でした。Reactはまだ知らないことも多いため、がんばっていこうとおもいます

コメント

タイトルとURLをコピーしました