React Hook Form 7.48.2へのアップデート:defaultValueの挙動変更への対応
背景
皆さん react-hook-form(以降 RHF)を使用していますか?
最近、バージョン 7.22.2 から 7.48.2 へとアップデートしたところ、resetFields のdefaultValue
オプションの動作が以前とは異なることに気づきました。
この問題に直面したのは、プロップスの変更を検知し、RHF で管理されている特定のフィールド値を更新したいと考えたときでした。以前のバージョンでは、useEffect を使用して resetField 関数で値の更新を行っていましたが、バージョンアップ後、この方法では期待通りに動作しなくなりました。
システムの概要を簡単に説明すると、1つのページに2つの独立したフォームを設置しており、それぞれが別々に設定および更新可能なUIを構築していました。また、簡単に仕様を説明すると以下の通りです:
- フォーム A では、開催日程(scheduledAt)を設定できる
- フォーム B では、承認期限(approvalDeadlineAt)を設定できる
- 承認期限が設定されている場合、開催日程は承認期限より後の日付でなければならない。つまり、フォーム A とフォーム B は「相関関係」にある
この3番目のバリデーションルールをzod
を用いて定義しようと考えました。そこで、フォーム A でのフィールド値を RHF で管理する際に、フォーム B の承認期限も一緒に管理するようにしました。以下は、その zod スキーマの概要です:
import { z } from 'zod';
const schema = z.object({
scheduledAt: z.date.nullable(),
approvalDeadlineAt: z.date().nullable()
}).superRefine(({ scheduledAt, approvalDeadlineAt }, ctx)) => {
// ...(バリデーションロジック)...
})
// 以降のコード
// ...
さらに、フォーム B で承認期限が変更されたとき、フォーム A の RHF で管理している承認期限の値も更新するようにしました。
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
// スキーマの定義
const schema = z.object({
// ...スキーマの詳細
});
type FormValue = z.infer<typeof schema>;
// フォーム A のカスタムフック
const useFormA = (defaultValues: FormValue) => {
const { approvalDeadlineAt } = defaultValues
// useFormの使用
const rhfMethods = useForm<FormValues>({
defaultValues,
resolver: zodResolver(schema)
});
const { resetField } = rhfMethods;
// 承認期限の変更を検知して、値を更新する
// この処理が期待する動きをしなくなった😱
useEffect(() => {
resetField('approvalDeadlineAt', { defaultValue: approvalDeadlineAt });
}, [approvalDeadlineAt, resetField]);
return {
rhfMethods
};
}
// フォーム A コンポーネント
const FormA = (args: { defaultValues: FormValues }) => {
const { register } = useHogeForm(args.defaultValues);
return (
<form>
<input type='date' {...register('scheduleAt')} />
</form>
);
};
結論
React Hook Form(RHF)のregister機能に関して、公式ドキュメントには以下のようなヒントがありました:
You can also register inputs with useEffect and treat them as virtual inputs.
公式より抜粋
このヒントに基づき、useEffect 内で register 関数を使用することにより、バージョンアップに伴う問題を解決しました。
// フォーム A のカスタムフック
const useFormA = (defaultValues: FormValue) => {
const { approvalDeadlineAt } = defaultValues
// useFormの使用
const rhfMethods = useForm<FormValues>({
defaultValues,
resolver: zodResolver(schema)
});
- const { resetField } = rhfMethods;
+ const { resetField, register } = rhfMethods;
+ useEffect(() => {
+ register('approvalDeadlineAt')
+ }, [register])
useEffect(() => {
resetField('approvalDeadlineAt', { defaultValue: approvalDeadlineAt });
}, [approvalDeadlineAt, resetField]);
return {
rhfMethods
};
}
まとめ
仮想の入力フィールドや条件分岐で描画される入力フィールドについては、useEffect 内で register 関数を使用することが有効です。
Discussion