React Hook Form: useFieldArray の append の挙動を調べてみた
この記事は何か
React Hook Form のuseFieldArrayを使用している際、フィールドを全削除してからappendを行ったときに defaultValues の値が参照される挙動になると気づきました。
こういう挙動だと予想していなかったので、なんでこうなるか探ってみました。
※ 検証バージョン: react-hook-form: 7.68.0
確認した挙動
useFormのdefaultValuesに配列データ(例: [{ name: 'coke', price: '' }])を設定し、useFieldArrayでその配列を管理しているとします。
この状態で、一度すべてのフィールドを削除し、新たにappend({ name: undefined, price: '100' })を実行して行を追加しました。
undefinedで投げたので、追加されたオブジェクトのnameプロパティはundefinedになるかなと思ってました。
でも実際には、defaultValuesで設定していた'coke'という値がセットされていました。
再現コードはこちら
長くなるので要所だけ簡易的に書きます
Next.js v15 で実行していました
const { control } = useForm({
defaultValues: {
items: [{ name: 'coke', price: '' }],
},
});
const { fields, append, remove } = useFieldArray({
control,
name: 'items',
});
// 1. 既存のフィールドをすべて削除
remove();
// 2. 新しいアイテムを追加(nameをundefinedとして渡す)
append({ name: undefined, price: '100' });
// 結果:
// items[0] は { name: 'coke', price: '100' } となる。
// 期待値の { name: undefined, price: '100' } ではない。
ライブラリのコードを確認してみた
react-hook-form: 7.68.0 のソースコードを確認して、この挙動に関係してそうなところを見てみました。
append 関数では最終的に _setFieldArray を経由して更新を行いますが、その過程で updateValidAndValue 関数が呼び出される形となってます。
そのupdateValidAndValue 関数は以下のようになっています。
const updateValidAndValue = (name, shouldSkipSetValueAs, value, ref) => {
const field = get(_fields, name);
if (field) {
// ここで値の解決を行っている
const defaultValue = get(_formValues, name, isUndefined(value) ? get(_defaultValues, name) : value);
// ... (省略)
}
};
上記の get 関数の第 3 引数はフォールバック値です。
value が undefined (isUndefined(value)) の場合、get(_defaultValues, name) が参照されるロジックになっています。
つまり、append({ name: undefined }) を実行した際、そのフィールドの値解決において「渡された値が undefined なら defaultValues から取得する」という処理になってます。
なるほどですね。
公式ドキュメントでの説明
公式ドキュメントにその仕様って書かれていたかなと思い、React Hook Form の公式ドキュメント(useFieldArray)を確認してみました。
自分の理解不足かもですか「undefined を渡すと defaultValues で補完される」と明記されている箇所は見当たりませんでした。
ただし、appendのルールとして以下の記述があります(Rules セクションより引用)。
When you append, prepend, insert and update the field array, the obj can't be empty object rather need to supply all your input's defaultValues.
(訳:フィールド配列への追加・挿入・更新時、オブジェクトは空であってはならず、すべての入力の defaultValues を供給する必要があります。)
また、append関数の説明にも以下の警告があります。
Important: append data is required and not partial.
(訳:重要:append するデータは必須であり、部分的(partial)であってはいけません。)
つまりは、ライブラリのスタンスとしては「追加するデータは省略せずにすべて渡すこと」が前提となっています。
自分の使い方が悪かったようです。
undefinedを渡すこと自体が「完全なデータを与えていない(Partial である)」状態とみなされ、結果としてdefaultValuesがフォールバックとして参照されるようですね。
この挙動を回避するには、undefinedの使用を避け、明示的な値(nullや空文字など)を使用するのが確実です。
まとめ
react-hook-form の詳細な挙動について学ぶいい機会になりました。
元はといえばドキュメントに沿った使い方をしていなかったのが原因でなので、あれ?と思ったら公式ドキュメントを見ているのがやはり大切だなと学べました。
あと、AI エージェントのおかげでこういうライブラリの実コードのチェックもとてもスムーズに行えてありがたいですね。
Discussion