React Hook FormでuseFieldArrayの初期化方法を間違っていた話。初期値はdefaultValuesで設定すること
React Hook FormのuseFieldArrayは動的にフォームの入力項目を増減する必要がある場合にとても便利な機能です。
とても便利な機能なのですが、私は最初間違って使っていました。
正しい使い方を理解したので記事にまとめておこうと思います。
useFieldArrayの公式ドキュメントは下記を参照してください。
どう間違えたのか?
サンプルコードを交えて説明した方が理解しやすいので、サンプルコードに沿って説明します。
サンプルコードのバージョンは下記の通りです。
- react: 18.2.0
- react-hook-form: 7.43.0
例えば下記のようなデータをAPIで受け取り、valueの数だけテキストボックスを作成する画面を考えます。
{
hoge: [
{ value: 100 },
{ value: 0 },
{ value: 10 },
{ value: 50 }
]
}
上記を取得したら次の画面を表示します。
間違った使い方
最初、replace
を使って下記のように実装しました。
import { useEffect } from 'react';
import { useFieldArray, useForm, } from 'react-hook-form';
export const Form = () => {
const {
control,
register,
formState: { isDirty },
} = useForm<{ hoge: { value: number }[] }>();
const { fields, replace } = useFieldArray({
name: 'hoge',
control,
});
useEffect(() => {
// APIで取得した結果を基にreplaceでfieldsを置き換える
// response = hoge: [
// { value: 100 },
// { value: 0 },
// { value: 10 },
// { value: 50 }
// ]
const response = hogeApi();
replace(response.hoge);
}, [replace]);
return (
<form>
{fields.map((field, index) => (
<div key={index}>
<input {...register(`hoge.${index}.value`)} />
</div>
))}
<button disabled={!isDirty}>更新</button>
</form>
);
};
下記の画面が表示され、ぱっと見は正しく表示されているように見えますが、よく見るとisDirtyが機能していません。
isDirtyは初期値はfalseで、値が編集されるとtrueになります。
<button disabled={!isDirty}>
と実装しているので、isDirtyがfalseの場合はボタンが無効化されるはずなのですが、最初から有効になっています。
正しく動作していないので間違った使い方だったとわかります。
正しい使い方
defaultValues
を活用して下記のように実装しました。
// 呼び出し元でhogeApi()を実行し、レスポンスをpropsで渡す
export const Form = ({ hoge }: { hoge: { value: number }[] }) => {
const defaultValues = { hoge };
const {
control,
register,
formState: { isDirty },
} = useForm<{ hoge: { value: number }[] }>({
defaultValues
});
const { fields } = useFieldArray({
name: 'hoge',
control,
});
return (
<form>
{fields.map((field, index) => (
<div key={index}>
<input {...register(`hoge.${index}.value`)} />
</div>
))}
<button disabled={!isDirty}>更新</button>
</form>
);
};
今回の場合、isDirtyが想定通りに動作して初期表示ではボタンが無効化されており、値を変更するとボタンが有効になります。
※ 注意点
defaultValuesは一度設定すると変更できないため、最初の処理のようにuseEffectで取得した値で後から変更することはできません。
そのため、APIで取得する処理を外出しして、propsで渡すようにしました。
react-hook-formの初期値について
react-hook-formの初期値はuseFieldArrayに限らずdefaultValuesを使うのが正しいやり方のようです。
defaultValuesを使っていなくてもそれっぽく動作しますが、isDirtyなど細かなところで上手く動かないことがあります。
React Hook Formを使っているけど、defaultValuesを使っていない場合はぜひ見直してみてください。
Discussion