『Formデータに編集済みデータがあれば、ブラウザの戻るが実行された場合に確認ダイアログを出す』というよくあるやつを出したくなって調べたところ、 この記事にあたりました。
わかりやすくていい記事だったので、やりたい人はこれを見てくださいで良いのですが、私はカスタムフックにして利用しました。
ちなみにnext/navigationの方のRouterだと、beforePopStateが存在しないので、appDirを使っている場合はEventListenerを使いましょう。
カスタムフック
useBackConfirm.ts
import { useState, useEffect } from "react"; const CONFIRM_MESSAGE = "保存されていないデータは削除されますが、よろしいですか?"; // setIsEditedにtrueが設定されていた場合、ページバック時に確認モーダルを表示する export const useBackConfirm = (message?: string) => { const [isEdited, setIsEdited] = useState(false); const handlePopstate = () => { const ok = confirm(message || CONFIRM_MESSAGE); if (ok) { setIsEdited(false); window.history.back(); } else { history.pushState(null, "", null); } }; useEffect(() => { if (isEdited) { // ダミー履歴を挿入して「戻る」を1回分吸収する history.pushState(null, "", null); window.addEventListener("popstate", handlePopstate, false); } return () => { window.removeEventListener("popstate", handlePopstate, false); }; }, [isEdited]); return { setIsEdited } as const; };
使い方
react-hook-formで利用する場合は、formState.isDirtyの値をuseEffectで見て、setIsEditedに値を設定すると楽です。
const { setIsEdited } = useBackConfirm(); const { register, formState: { isDirty } } = useForm<SampleForm>(); useEffect(() => { if (isDirty) { setIsEdited(true); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [isDirty]);
このサンプルコードではダイアログのメッセージを変えたい場合にも対応しましたが、 この手の確認メッセージを一部だけ変えたいというのは実運用だと無いと思うので、パラメータは無い方が利用時に迷わなくて良いかもしれません。