🤏

React Hook Formで、フラグを次のページに引き継ぐコツ

に公開

複数ステップにまたがるフォームを実装する際、ページ間でフラグを管理したいときがあると思います。
今回は、ReactHookFormを使って実装する際のフラグの受け渡し方を紹介してみます。

フラグの管理で最初に思い浮かぶのは、localStorageやsessionStorageです。
これらでも状態を保持することはできるのですが、リロードしてもデータが残ってしまったり、割と手間だったりするのですが、今回はフラグをsetValueでセットする方法をとってみたいと思います。
↓こちらが1ページ目です。(app/form/1/page.jsx)

"use client";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
import { useFormContext } from "react-hook-form";

export default function Home() {
  const { register, watch, setValue } = useFormContext();
  const router = useRouter();

  const planName = watch("planName") || "";
  const isCurrentPlan = watch("isCurrentPlan") || "";

  useEffect(() => {
    console.log("選択されたプラン:", planName);
    console.log("現在のプランかどうか", isCurrentPlan);
  }, [watch()]);

  const handleNextPage = (e) => {
    e.preventDefault();
    router.push("./2");
  };

  return (
    <div className="p-10">
      <form onSubmit={handleNextPage} className="flex flex-col gap-4 max-w-md">
        <div className="flex flex-col gap-2">
          <label className="text-lg font-semibold">プラン選択</label>
          <div className="flex gap-4">
            <label
              className={`cursor-pointer py-2 px-4 rounded-md shadow text-lg w-full text-center
                ${
                  planName === "Aプラン"
                    ? "bg-black text-white"
                    : "bg-gray-300 text-gray-800"
                }`}
            >
              <input
                type="radio"
                value="Aプラン"
                onClick={() => setValue("isCurrentPlan", "true")}
                {...register("planName")}
                className="sr-only"
              />
              プランA
            </label>
            <label
              className={`cursor-pointer py-2 px-4 rounded-md shadow text-lg w-full text-center
                ${
                  planName === "Bプラン"
                    ? "bg-black text-white"
                    : "bg-gray-300 text-gray-800"
                }`}
            >
              <input
                type="radio"
                value="Bプラン"
                onClick={() => setValue("isCurrentPlan", "false")}
                {...register("planName")}
                className="sr-only"
              />
              プランB
            </label>
          </div>
        </div>

        <button
          type="submit"
          className="bg-red-600 text-white py-2 px-4 rounded-md shadow"
        >
          次のページへ
        </button>
      </form>
    </div>
  );
}

こちらが2ページ目です。(app/form/2/page.jsx)

"use client";
import { useEffect } from "react";
import { useFormContext } from "react-hook-form";

export default function Home() {
  const { watch } = useFormContext();

  const selectedPlan = watch("isCurrentPlan") || "";

  useEffect(() => {
    // 現在のプランが選択された場合にfetchを実行
    if (selectedPlan === "true") {
      // fetch APIを使ってデータを取得
      fetch("/api/getCurrentPlanData")
        .then((response) => response.json())
        .then((data) => {
          console.log("現在のプランのデータ: ", data);
        })
        .catch((error) => {
          console.error("API呼び出しエラー: ", error);
        });
    }
  }, [selectedPlan]);

  return (
    <div className="p-10">
      <p>
        <span className="font-bold text-lg">
          {selectedPlan === "true" ? "現在のプラン" : "他のプラン"}
        </span>
        を選択しました。
      </p>
    </div>
  );
}

コードの概要としては、プランAが現在のプラン、プランBが他のプランです。
planNameだけだと、そのプランの名前しかデータとして扱えていませんでしたが、isCurrentPlanというフラグをsetValueでセットすることで、一つのボタンで2つの情報を持たせています。

1ページ目でプランAを選択すると、コンソールで「true」と表示されている。

すると、2ページ目でしっかり反映されている。

今回のように、ボタンを押した際にsetValueを追加することで、一つではなく、複数のフラグをセットすることもできます。
ただし、何も考えずsetValueでセットするのではなく、あくまでフォームに関連するフラグのみをセットするのがよいと思います。(責務の分離)フォーム以外で状態を管理したいときは、ContextやRecoilを使ったほうが良いと思います。

Discussion