【React】ReactHookFormのuseFieldArrayで可変フォームの実装

はじめに

この記事の概要

こんにちは、株式会社TOKOSのスギタです!
本日はReactHookFormで実装する可変フォームの紹介をします。

可変フォームでもReactHookFormを使用すれば簡単にお手軽に実装できてしまいます!
ReactHookFormを使用したことがない方は下記を一度参照することをおすすめします!

対象読者

  • フロントエンドエンジニアの方
  • ReactHookFormを使用している方

開発環境

今回はスタイリングはtailwindを使用しています。
今回tailwindの使用方法は省きます

  • react-hook-form: “^7.51.5”,
  • react: “^18”,
  • tailwindcss: “^3.4.1”,
  • typescript: “^5”

実装完了予定

今回入力フォームが増減するフォームを作成していきたいと思います。

今回の完成予定は下記になります。

このように可変するフォームを作成していこうと思います。

早速コードの紹介です。

useFormの使用

export default function ExamplePage() {
  const { register, control, handleSubmit } = useForm({
    defaultValues: {
      name: { firstName: "", lastName: "" },
    },
  })

  return (
    <form onSubmit={handleSubmit((data) => console.log(data))}>
      <ul className="flex flex-col gap-2">
        <li key={item.id} className="flex gap-2">
          <input {...register("name.firstName"} className="border  border-black" />
          <input {...register("name.lastName"} className="border border-black" />
        </li>
      </ul>
      <button type="submit" className="mt-2 w-[300px] bg-blue-500 p-2 text-white">
        送信
      </button>
    </form>
  )
}

まずはuseFormを使用していつも通りフォームを実装してください。
今回最終的な取得したいバリューはtest:[{firstName:string,lastName:string}]ですが一旦通常の単一フォームで実装しています。
名字と名前の入力するフォームを想定しています。
defaultValueは空文字にしています。

可変フォームの実装

React Hook Formで可変フォームを実現するにはuseFieldArray を使用しなければなりません。
最終的な取得したい値が配列で要素数が可変させたい場合に使用します!

注意

初期状態でフォームを表示させたい場合、defaultValuesで初期値を指定してください。
今回は初期状態で表示させたいためdefaultValueで指定しています。

export default function ExamplePage() {
  const { register, control, handleSubmit } = useForm({
    defaultValues: {
      name: [{ firstName: "", lastName: "" }],
    },
  })
  const { fields, append, remove } = useFieldArray({
    control,
    name: "name",
  })

  return (
    <form onSubmit={handleSubmit((data) => console.log(data))}>
      <button
        className="mb-3 w-[300px] bg-green-500 p-2 text-white"
        type="button"
        onClick={() => append({ firstName: "", lastName: "" })}
      >
        追加する
      </button>
      <ul className="flex flex-col gap-2">
        {fields.map((item, index) => (
          <li key={item.id} className="flex gap-2">
            <input {...register(`name.${index}.firstName`)} className="border  border-black" />
            <input {...register(`name.${index}.lastName`)} className="border border-black" />
            <button
              className="bg-red-500 p-2 text-white"
              type="button"
              onClick={() => remove(index)}
            >
              削除する
            </button>
          </li>
        ))}
      </ul>
      <button type="submit" className="mt-2 w-[300px] bg-blue-500 p-2 text-white">
        送信
      </button>
    </form>
  )
}

useFieldArrayを呼びだします。
引数にはcontrolnameを渡します。
controluseFormを使用している場合は必須で、nameは必須になっています。
今回デフォルトバリューを使用して初期状態を指定しているため、controlは必須になります。

その他詳細が知りたい方は下記を参照してください。

今回このuseFieldArrayfieldsが重要になってきます。
可変させたいフォームをfieldsで繰り返し処理(map)することにより、可変フォーム化することができます。
map処理の第二引数のindexregistername属性に渡し、一意なnameにすることにより可変フォーム化するということです。

注意

fieldsでmapしている要素のkeyはfield.idにしてください。

またuseFieldArrayにメソッドが用意されております。
追加する(append)削除(remove)これらを使用することで配列内の要素を増減することができます。

appendremoveの他にも配列内の要素を扱うためのメッソドが用意されております。

appendフィールドの最後(配列の最後)に指定したデータを追加してフォーカスします。
remove特定の位置(index)にある入力を削除します。
prependフィールドの先頭(配列の最初)に指定したデータを追加してフォーカスします。
insert特定の位置(index)に指定したデータを追加してフォーカスします。
swap位置の入れ替え
move指定した位置に移動
update特定の位置で入力を更新すると、更新されたフィールドはマウント解除され、再マウントされます。
replace配列の値全体を置き換え

このように実装条件により、様々なメソッドを選択することができます。

通常の可変フォームですと、appendremoveを使用すれば問題ないかと思います!

さいごに

今回はReactHookFormで可変フォームを作成してみました。
やはりReactHookFormは様々なHookが用意されており、とても助かるなという印象です。

可変フォームなど少々複雑でも簡単に実装できていまい大変助かります!