1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

この記事では、Reactにおいてフォームのバリデーション管理ができる、React-Hook-Formライブラリ(以降はRHF)において、プリミティブ型の配列のバリデーションを行う方法について記述します。

対象となる方

  • react-hook-formを利用して、string[]のような、動的な配列のバリデーションをしたい人

Reack Hook Formとは

Reack Hook Form(以降はRHF)とは、Reactアプリケーションにおいて、フォームのバリデーションを簡単に実装できるライブラリです。
その中で、動的な配列を簡単に実装できるフックとして、useFieldArray()が提供されています。
https://react-hook-form.com/docs/usefieldarray

useFieldArray

ドキュメントのexampleにあるように、fieldsが配列自体、appendremoveが配列に要素追加・削除する関数として取得できます。
これによって、TODOリストや参加者リストなど、配列の要素数が定まっていない値に対しても、RHFによるバリデーションの恩恵を受けることができます。

実装例
export const FieldArray = () => {
  const { register, control } = useForm({
    defaultValues: {name: 'defaultUser', age: 18}
  });
  const { fields, append, remove } = useFieldArray({
    control,
    name: "hogeList"
  });

  const handleAdd = () => {
    append({name: 'testUser', age: 20})
  }
  
  return (
      <>
        {fields.map((item, index) => (
          <div key={item.id}>
            <input {...register(`hogeList.${index}.name`)} />
            <input {...register(`hogeList.${index}.age`)} />
            <button type="button" onClick={() => remove(index)}>Delete</button>
          </div>
        ))}
        <button type="button" onClick={handleAdd}>ADD</button>
    </>
  );
}

ただし、useFieldArrayが利用可能な配列の種類は、オブジェクトの配列のみです。つまり、キーとバリューを持つデータ構造でなければなりません。

問題点

先の説明の際に、useFieldArrayを利用することで、TODOリストもバリデーション可能となると述べました。
ただし、useFieldArrayを利用するには、プリミティブの配列ではなく、オブジェクトの配列にする必要があります。

1. todoList: string[] // プリミティブの配列では動かない
2. todoList: { title: string }[] // オブジェクトの配列は動く

2番の実装のように、オブジェクトのvalueを管理したい値にすることで、useFieldArrayを利用したバリデーションを実装することは可能です。
しかし、TODOリストとしての仕様は、文字列の配列で満たすことができるはずです。
それが、ライブラリの都合のみによって、実装が変わってしまうことは、可能であれば避けたいなと個人的には思います。

そこで、まずは簡単な解決策を考えてみました。
useFieldArray使わずにmapでゴリ押すパターンです。

実装例


export const TodoList = () => {
  const { register, setValue } = useForm({
    defaultValues: { list: [''] }
  });
  
  return (
    <>
      {list.map((value: string, index: number) => {
        return (
          <>
            <input key={index} {...register(value)} /> 
            <button onClick={() => setValue('list', details.toSpliced(index, 1))} >Delete</>
          </>
          )
      })}
      <button onClick={() => setValue('list', [...list, ''])}>Add</button>
    </>
}

ただし、削除・挿入の際に起きる再レンダリング範囲が、useFormを利用してバリデーション実装をしているコンポーネントすべてになってしまうという課題が存在してしまいます...。
フォームの部品すべてが再レンダリングの範囲に含まれてしまうことは、特にフォームの入力要素数が多い場合には、望ましいことではありません。 

結論

この記事では、RHFを利用して、動的なプリミティブの配列を扱うときの実装方法について述べていきました。
実現方法としては2つ存在しています。

  1. 管理したい値をvalueとしたオブジェクト型にして、useFieldArrayを利用する
  2. useFieldArrayを利用せずに、mapを用いる

それぞれ、メリット・デメリットが存在するため、管理するフォームの規模や実装方針に合わせて選定すると良いと思いました。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?