🌴

【React】React Hook FormのuseFieldArrayで配列配下の項目のエラーを取得

2022/10/30に公開2

概要

reactでフォーム入力のライブラリReact Hook Formでは、可変の配列項目を格納できるuseFieldArrayという機能があります。このuseFieldArrayで配列配下の項目がバリデーションエラーになった時に、そのエラーをどう取得するかというメモ書きです。

前提

対応

Dynamically add errors when using useFieldArray with React Hook Formのstackoverflowの記事で対応が紹介されている通り、errors.items.index.nameの形式でエラーを参照することができます。

実装サンプル

用意した配列(sampleRecords)へ、テキスト項目(name)を入力するものを、サンプルとして実装します。

まずは配列の定義部分の実装です。

SampleNameInputComponent.js
import { useEffect } from "react";
import useSWR from "swr";
import { useFieldArray, useForm } from "react-hook-form";
import toast from "react-hot-toast";

import SampleNameInputComponent from "./input/SampleNameInputComponent";

export default function SampleArrayComponent() {
  const filedArrayName = "sampleRecords";
  const { control, register, formState } = useForm({
    mode: "onChange",
  });
  const { fields, replace } = useFieldArray({
    control,
    name: filedArrayName,
  });

  const { data: sampleRecords } = useSWR("/getRecords", async () => {
    // API経由で初期値を取得(記載は割愛)
    const result = await getFromAPI();
    if (result.status != 200) {
      toast.error("データの取得に失敗しました", {
        duration: 3000,
      });
      return undefined;
    } else {
      return result.data;
    }
  });

  useEffect(() => {
    if (sampleRecords) {
      // 取得したデータをフォームに設定
      replace(
        sampleRecords.map((record) => {
          return {
            name: record.name,
          };
        })
      );
    }
  }, [sampleRecords]);

  return (
    <>
      {sampleRecords && (
        <>
          {fields.map((_, index) => (
            <div>
              <SampleNameInputComponent
                fieldArrayName={filedArrayName}
                fieldName="name"
                index={index}
                register={register}
                errors={formState.errors}
              />
            </div>
          ))}
        </>
      )}
    </>
  );
}

次はテキストボックスのコンポーネントの実装です。ここでエラーの表示を行います。

SampleNameInputComponent.js
export default function SampleNameInputComponent(prop) {
  const fieldName = `${prop.fieldArrayName}.${prop.index}.${prop.fieldName}`;
  // エラーメッセージの取得
  const errorMessage =
    prop.errors?.[prop.fieldArrayName]?.[prop.index]?.[prop.fieldName]?.message;
  return (
    <>
      <input
        id={fieldName}
        type="text"
        name={fieldName}
        {...prop.register(fieldName, {
          required: "名前は必須項目です",
          maxLength: {
            value: 300,
            message: "300文字以下で入力してください",
          },
        })}
      />
      {errorMessage && <div>{errorMessage}</div>}
    </>
  );
}

Discussion

nap5nap5

フォームデータ定義のうち、配列リストの単一アイテムごとにzodで外出したバリデーション結果を表示するようなサンプルを作ってみました。

https://codesandbox.io/p/sandbox/upbeat-paper-67fqt2?file=%2Fsrc%2Ffeatures%2Fstory%2Fcomponents%2FForm.tsx&selection=[{"endColumn"%3A1%2C"endLineNumber"%3A12%2C"startColumn"%3A1%2C"startLineNumber"%3A12}]

簡単ですが、以上です。

なかつがわなかつがわ

ありがとうございます。
zodで外出しすると、コードが整理されて綺麗になりますね。