🕟
【React Hook Form】useFieldArrayで付与されるkey属性の値について(2024年3月時点)
概要
React Hook Formで配列の値を制御したい時、useFieldArray
を使うと思いますが、その際に使われるkey属性の値内容についてメモ書きします。なお2024年3月時点で、ドキュメントにName of the attribute with autogenerated identifier to use as the key prop. This prop is no longer required and will be removed in the next major version.
と記載がある通り、今後の更新で動作が変わる可能性が高いので、あくまで現時点での動作という形になります。
前提
- 今回使用したReact Hook Formのバージョンは
7.51.0
になります。
動作内容
React Hook Form useFieldArray overwriting my own idのstackoverflowの記事にある通り、key属性に設定した項目名(デフォルトだと「id」)について、useFieldArrayのフィールドでは値を自動で上書きを行う動作になっているようです。ただ、元のフォームの値には反映されずに、あくまでuseFieldArrayでのフィールドのみに適用されるようです。もし、元のフォームで「id」属性を使いたい場合は、useFieldArrayで別名のkeyを指定したほうがベターだと思います。
実装サンプル
今回、uiはshadcn/ui
を使用してサンプルを実装しました。
以下の実装では「id」属性をあえてformでも使用するようにして、値がどのように設定されるか確認しました。
export const sampleInputFormSchema = z.object({
memoList: z.array(
z.object({
id: z.string().optional(),
title: z.string().optional(),
contents: z.string().optional(),
})
),
});
export const SampleInputComponent: FC = () => {
const form = useForm<z.infer<typeof sampleInputFormSchema>>({
resolver: zodResolver(sampleInputFormSchema),
mode: "onSubmit",
reValidateMode: "onSubmit",
defaultValues: {
memoList: [{ id: "test" }],
},
});
const control = form.control;
const {
fields: memoFields,
prepend: memoPrepend,
remove: memoRemove,
} = useFieldArray({
control,
name: "memoList",
});
const submitFunc = async (
data: z.infer<typeof sampleInputFormSchema>
) => {
// useFieldArrayのfieldsではformの値ではなく自動で割り当てられたid値で上書きされる
console.log(memoFields);
// 元のformにはuseFieldArrayのid値は反映されない
console.log(data);
};
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(submitFunc)}>
<div className="flex flex-col gap-4 ml-2">
<div className="flex flex-row gap-2 items-center">
<FormLabel>メモ</FormLabel>
<Button
onClick={() => {
memoPrepend({
id: "test",
});
}}
type="button"
>
メモを追加
</Button>
</div>
{memoFields.map((memo, index) => (
<>
{/* ここのidはuseFieldArrayで割り当てられる値 */}
<div key={memo.id}>
<FormField
control={form.control}
name={`memoList.${index}.title`}
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
placeholder="メモのタイトルを入力"
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name={`memoList.${index}.contents`}
render={({ field }) => (
<FormItem>
<FormControl>
<Textarea
{...field}
placeholder="メモの内容を入力"
/>
</FormControl>
</FormItem>
)}
/>
<Button
onClick={() => {
memoRemove(index);
}}
type="button"
>
メモを削除
</Button>
</div>
</>
))}
</div>
<Button type="submit">
<p>結果を登録</p>
</Button>
</form>
</Form>
);
};
Discussion