React Routerで画面遷移前に破棄確認ダイアログを表示する
本記事で使用しているReact Routerのバージョンは6.27.0
です。6.4.0
より前のバージョンではこれから紹介するコードは動作しません。
破棄確認ダイアログとは
フォームなどを入力して保存する前にリロードやページ遷移をしようとした時に表示される以下のようなダイアログのことをここでは指します。
React Routerで破棄確認ダイアログを実装する
import { useCallback, useState } from "react";
import { unstable_usePrompt, useBeforeUnload } from "react-router-dom";
const message = "行った変更が保存されない可能性があります。";
export function Form() {
const [name, setName] = useState("");
const isDirty = name !== "";
// ページリロードやSPAではないページに遷移するときに確認ダイアログを表示する
useBeforeUnload(
useCallback(
(event) => {
if (!isDirty) {
return;
}
if (window.confirm(message) === false) {
// Cancel the event as stated by the standard.
event.preventDefault();
// Chrome requires returnValue to be set.
event.returnValue = "";
}
},
[isDirty]
)
);
// SPA遷移するときに確認ダイアログを表示する
unstable_usePrompt({
message,
when: ({ currentLocation, nextLocation }) =>
isDirty && currentLocation.pathname !== nextLocation.pathname,
});
return (
<form>
<label>
Name:
<input
onChange={(event) => {
setName(event.target.value);
}}
value={name}
type="text"
/>
</label>
<button type="submit">Submit</button>
</form>
);
}
実際の挙動
解説
上記のコードでは確認ダイアログを出すために2つのhookを使用しています。1つ目はuseBeforeUnload です。
このhookを使用することで beforeunloadイベントが発生した時の挙動を制御することができます。beforeunloadイベント
はページリロードやタブ閉じ、SPAではないページに遷移する時などに発生するイベントのため、ユーザーがそれらの行動を行なった際に確認ダイアログを表示することができます。このhookの内部実装は addEventListener
で beforeunloadイベント
を補足しているだけなのでこれを必ず使う必要があるかと言われるとそうではないです。
2つ目はunstable_usePromptです。
このhookを使用することでSPA遷移を補足することができ、遷移前に確認ダイアログを表示することができます。SPA遷移は History API を使用しているため、useBeforeUnload
ではイベントを補足することができません。そのため、そちらとは別にこちらのhookが必要になります。
落とし穴 🕳️
今回の実装で使用したunstable_usePrompt
や unstable_usePrompt
の内部で使用されているuseBlockerなどのhookを使用するためには、createBrowserRouterなどの関数でルートを定義する必要があります。昔ながら?のBrowserRouterなどのRouter Componentによってルートを定義している場合は以下のようなエラーが発生するので注意が必要です。
Uncaught Error: useBlocker must be used within a data router. See https://reactrouter.com/routers/picking-a-router.
ちなみにこのエラー文に記載されているリンクが切れていたので修正のPRを出してみたらマージしてもらえました😄
まとめ
React Routerで破棄確認ダイアログを表示する方法をご紹介しました。個人的に unstable_usePrompt
の引数の when
の命名がカッコイイなと思ったので、どこかで真似したいです。
Discussion