🗓️
React Hook FormのonBlurがDatePicker(MUI)に効かない時の解決方法
はじめに
useForm
の mode
を onBlur
に設定した際に発生した DatePicker
コンポーネントの挙動不良について調査した結果をまとめてみました。
問題について
useForm
の mode
を onBlur
に設定したところ、DatePicker
コンポーネントが正しく動作しないことに気づきました。
調査を進めた結果、DatePicker
にはそもそも onBlur
プロパティが存在しないことがわかりました。
type Props<T extends FieldValues> = {
control: ControlForm<T>;
name: Path<T>;
rules: Omit<RegisterOptions<FieldValues>, 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'>;
};
export const Calendar = <T extends FieldValues>({ name, control, rules }: Props<T>) => {
return (
<Controller
name={name}
control={control}
rules={rules}
render={({ field, fieldState }) => {
return (
<LocalizationProvider dateAdapter={AdapterDateFns} adapterLocale={ja}>
<FormControl error={fieldState.error ? true : false} className="ml-sm">
<DatePicker
{...field}
// onBlueは存在しない
// onBlur = {field.onBlur}
/>
</FormControl>
</LocalizationProvider>
);
}}
/>
);
};
そこでDatePicker
の内部で使用しているtextField
にfield.onBlur
を渡すことにしました。
DatePicker
のプロパティであるslotProps
を使うと、内部で使用されるtextField
をカスタマイズすることができます。
ちなみにerror
やhelperText
もslotProps
で指定します。
type Props<T extends FieldValues> = {
control: ControlForm<T>;
name: Path<T>;
rules: Omit<RegisterOptions<FieldValues>, 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'>;
};
export const Calendar = <T extends FieldValues>({ name, control, rules }: Props<T>) => {
return (
<Controller
name={name}
control={control}
rules={rules}
render={({ field, fieldState }) => {
return (
<LocalizationProvider dateAdapter={AdapterDateFns} adapterLocale={ja}>
<FormControl error={fieldState.error ? true : false} className="ml-sm">
<DatePicker
{...field}
slotProps={{
calendarHeader: { format: 'yyyy/MM' },
textField: {
helperText: fieldState.error.message ?? ' ',
error: fieldState.error ? true : false,
// ここで渡す。
onBlur: field.onBlur,
},
}}
/>
</FormControl>
</LocalizationProvider>
);
}}
/>
);
};
これで解決、、?
これで解決かと思いましたが、実際に操作してみると次のような問題が発生しました。
- エラーが表示されている状態
- 日付を選択する
- カレンダーが非表示になり、
DatePicker
からフォーカスが外れる(この時点でエラーが消えてほしい) - 再度
DatePicker
にフォーカスを当てる - ようやくエラーが消える
検討した結果、field.onBlur
を onAccept
へ渡すことにしました。
onAccept
は、日付の選択を確定した際に呼び出されるイベントで、ここに field.onBlur
を設定し、日付選択後にフォーカスが外れたタイミングでエラーが消えるようにしました。
type Props<T extends FieldValues> = {
control: ControlForm<T>;
name: Path<T>;
rules: Omit<RegisterOptions<FieldValues>, 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'>;
};
export const Calendar = <T extends FieldValues>({ name, control, rules }: Props<T>) => {
return (
<Controller
name={name}
control={control}
rules={rules}
render={({ field, fieldState }) => {
return (
<LocalizationProvider dateAdapter={AdapterDateFns} adapterLocale={ja}>
<FormControl error={fieldState.error ? true : false} className="ml-sm">
<DatePicker
{...field}
slotProps={{
calendarHeader: { format: 'yyyy/MM' },
textField: {
helperText: fieldState.error.message ?? ' ',
error: fieldState.error ? true : false,
onBlur: field.onBlur,
},
}}
// ここで渡す。
onAccept={field.onBlur}
/>
</FormControl>
</LocalizationProvider>
);
}}
/>
);
};
まとめ
実は、他の MUI
のコンポーネントでも同じように react-hook-form
の onBlur
が効かないことがあるので、こういった調整が必要になるかもしれません。
コンポーネントごとの挙動を理解しながら、より使いやすいフォームを作っていくのが大事ですね!✨
Discussion