動的フォーム

1import { useFieldArray, useForm } from 'react-hook-form';
2import { HiOutlineMinusCircle, HiOutlinePlusCircle } from 'react-icons/hi';
3
4const DynamicForm = () => {
5  const {
6    register,
7    control,
8    watch,
9    formState: { errors },
10  } = useForm({
11    defaultValues: {
12      title: '',
13      tags: [
14        {
15          value: '',
16        },
17      ],
18    },
19  });
20
21  const { fields, insert, remove } = useFieldArray({
22    control,
23    name: 'tags',
24  });
25
26  return (
27    <div className="grid grid-cols-1 lg:grid-cols-2">
28      <form>
29        <h2 className="mb-2">タイトル</h2>
30        <input
31          type="text"
32          autoComplete="off"
33          className="bg-transparent rounded border mb-6"
34          {...register('title')}
35        />
36        <h2 className="mb-2">タグ</h2>
37        <div className="space-y-2">
38          {fields.map((field, index) => (
39            <div key={field.id}>
40              <div className="flex items-center space-x-2">
41                <input
42                  className="bg-transparent rounded border"
43                  type="text"
44                  autoComplete="off"
45                  {...register(`tags.${index}.value`, {
46                    required: '必須入力です',
47                    maxLength: {
48                      value: 80,
49                      message: '最大80文字です',
50                    },
51                  })}
52                />
53                {fields.length > 1 && (
54                  <button type="button" onClick={() => remove(index)}>
55                    <HiOutlineMinusCircle className="text-xl opacity-60" />
56                  </button>
57                )}
58                <button
59                  onClick={() =>
60                    insert(index + 1, {
61                      value: '',
62                    })
63                  }
64                  type="button"
65                >
66                  <HiOutlinePlusCircle className="text-xl opacity-60" />
67                </button>
68              </div>
69              {errors.tags?.[index]?.value && (
70                <p className="text-red-500 text-sm">
71                  {errors.tags?.[index]?.value?.message}
72                </p>
73              )}
74            </div>
75          ))}
76        </div>
77      </form>
78      <div>
79        <pre>{JSON.stringify(watch(), null, 2)}</pre>
80      </div>
81    </div>
82  );
83};
84
85export default DynamicForm;

使用ライブラリ

参考サイト