旧ブログ(ISSEN)から移行しました

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

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

杉田侑祐
杉田侑祐4分で読めます

はじめに

この記事の概要

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

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

React Hook Form - performant, flexible and extensible form library

Performant, flexible and extensible forms with easy-to-use validation.

react-hook-form.com

対象読者

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

開発環境

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

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

実装完了予定

今回は入力欄が増減するフォームを作成していきたいと思います。
今回の完成予定は下記になります。

このように可変するフォームを作成していこうと思います。
早速コードの紹介です。

useFormの使用

React
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 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を使用していつも通りフォームを実装してください。
今回最終的に取得したいバリューはname:\[{firstName:string,lastName:string}\]ですが一旦通常の単一フォームで実装しています。
名字と名前の入力するフォームを想定しています。
defaultValuesは空文字にしています。

可変フォームの実装

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

React
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は必須になっています。
今回はdefaultValuesで初期状態を指定しているため、controlを渡しています。

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

useFieldArray

Performant, flexible and extensible forms with easy-to-use validation.

react-hook-form.com

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

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

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

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

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

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

さいごに

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

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

この記事を書いた人

杉田侑祐
杉田侑祐

TOKOSのフロントエンドエンジニア兼UI/UXデザイナー。このブログではフロントエンドメインで投稿しています。HIPHOPとゲームが好きです✌️