フォームの離脱を防ぐ

最終更新: 2022年02月02日

離脱を防ぐ必要性

長いフォームや長文の入力中に間違ってタブを閉じたり、ページを更新してしまうことがあります。その際に入力中のデータがすべて消失してしまったとしたらユーザーは相当なストレスを感じることになります。

ページを更新したり離れようとすることを離脱といいますが、フォーム編集中に離脱しようとしたらユーザーの確認アラートを挟むべきです。

離脱には

  • ブラウザ操作による離脱(リロードやタブ、ウィンドウを閉じる)
  • アプリ内の離脱(別のページに遷移する)

があり、その二つについて対策する必要があります。

実装

まずは使いまわせるよう離脱ガード用のカスタムフックを作成します。このフックの引数に true が渡る状態で離脱しようとすると確認アラートが表示されます。

hooks/form-guard.tsx
import { useRouter } from 'next/router'; import { useEffect } from 'react'; export const useFormGuard = (isDirty: boolean) => { const router = useRouter(); const message = '編集内容がリセットされます、本当にページ遷移しますか?'; const pageChangeHandler = () => { const answer = window.confirm(message); if (!answer) { throw 'routeChange aborted.'; } }; const beforeUnloadhandler = (event: BeforeUnloadEvent) => { event.preventDefault(); event.returnValue = message; }; useEffect(() => { if (isDirty) { // Next.jsのページ遷移を防ぐ router.events.on('routeChangeStart', pageChangeHandler); // ブラウザの離脱を防ぐ window.addEventListener('beforeunload', beforeUnloadhandler); return () => { router.events.off('routeChangeStart', pageChangeHandler); window.removeEventListener('beforeunload', beforeUnloadhandler); }; } }, [isDirty, router]); };

フォームコンポーネントで以下のように呼び出します。この例ではReact Hook Formを使っています。

const { reset, formState: { isDirty }, } = useForm(); useFormGuard(isDirty); const submit = () => { // 送信が成功した後に編集状態をリセットする reset(undefined, { keepValues: true, }); }

React Hook Form は登録したフォームに手が加えられたら isDirtytrue になります。この状態で離脱しようとすると確認アラートが表示されます。送信後は reset により isDirtyfalse になるので確認なく離脱できるようになります。