Laravel×React Inertiaを使ってフォーム変更時に画面遷移の確認ダイアログを簡単に実装する

はじめに

Laravel, Reactでユーザーの登録機能を作っています。

登録フォーム画面を開いた時「やっぱりキャンセルして前の画面に戻りたい」といった状況のためにキャンセルボタンを実装しようと思いました。

この時、フォームに入力・変更がある場合はページ遷移前に本当にキャンセルするか確認を促すようにします。Inertiaを使ってかなり簡単にできたためメモします。

ユーザー登録画面route('users.create')で、「キャンセルボタン」を押した時に登録画面から一覧画面route('users.index')に戻す処理を実装する例です。

// 実装コード概要
import { Link, useForm } from "@inertiajs/react";

const { isDirty } = useForm({ ... })

function handleBeforeLeave() {
  if (isDirty) {
    return confirm('入力内容が破棄されますがよろしいですか?');
  }
  return true;
};

return (
  <>
    <Link
      onBefore={handleBeforeLeave}
      href={route('users.index')}
      className="btn btn-secondary"
    >       
      キャンセル
    </Link>
    以下省略...
  </>
);

処理の説明

userFormのisDirty

useFormisDirtyプロパティを使います。参考: Forms - Inertia.js

isDirtyはフォームに変更がある場合にtrueを返します。

import { useForm } from '@inertiajs/react'

const { isDirty } = useForm({ ... })

LinkコンポーネントのonBefore

Inertiaの<Link>コンポーネントを使います。参考: Links - Inertia.js

Linkが持っているプロパティの一つonBeforeにコールバック関数を渡して、trueの時のみリンク処理を続行、falseの時は処理を中断させます。

import { Link } from "@inertiajs/react";

<Link
  onBefore={handleBeforeLeave}
  href={route('users.index')}
  className="btn btn-secondary"
>

routeメソッドはZiggyパッケージをインストールしてJSXファイルで使えるようにしています)

コールバック関数の定義

onBeforeに渡すコールバック関数を定義します。isDirtyにより、フォームに変更がある場合のみconfirmで確認ダイアログを表示し、ここでキャンセルした時だけfalseが返るためonBeforeによってLinkの処理が中断されます。

function handleBeforeLeave() {
  if (isDirty) {
    return confirm('入力内容が破棄されますがよろしいですか?');
  }
  return true;
};

実装例

ユーザー登録フォームCreate.jsxの該当部分抜粋です。

// resources/js/Pages/User/Create.jsx
import { useEffect } from 'react';
import AppLayout from '@/Layouts/AppLayout';
import { Link, useForm } from "@inertiajs/react";

export default function Create() {
  const { data, setData, post, processing, errors, reset, isDirty } = useForm({
    name: '',
    email: '',
    password: '',
    password_confirmation: '',
  });

  useEffect(() => {
    return () => {
      reset('password', 'password_confirmation');
    };
  }, []);

  function submit(e) {
    e.preventDefault();
    post(route('users.store'));
  };

  function handleBeforeLeave() {
    if (isDirty) {
      return confirm('入力内容が破棄されますがよろしいですか?');
    }
    return true;
  };

  return (
    <AppLayout>
      <h1>ユーザー登録</h1>
      <div className="form-action-bar">
        <Link
          onBefore={handleBeforeLeave}
          href={route('users.index')}
          className="btn btn-secondary"
        >
          キャンセル
        </Link>
        <button
          type="submit"
          form="userCreateForm"
          className="btn btn-primary"
          disabled={processing}
        >
          登録
        </button>
      </div>
      <form id="userCreateForm" onSubmit={submit}>
      ... 以下省略