Formik Field组件集成React-Datepicker后无法手动输入日期问题求解
问题原因
- 额外维护了独立于Formik的
date本地状态,同时通过useEffect监听date变化同步给Formik。react-datepicker手动输入过程中会逐字符触发onChange回调,未输入完成的不完整日期会被提前格式化同步给Formik,反过来覆盖日期选择器的输入值,触发日期选择器的自动补全逻辑,生成当月1号的日期。 datepickerComp定义在父组件内部,每次父组件渲染都会生成新的组件实例,导致Formik的Field组件频繁重制子组件状态,打断输入流程。- 手动调用
setFieldError不生效是因为调用时机和Formik的自动校验逻辑冲突:Formik默认会在change、blur事件后自动触发校验,手动设置的error会被后续自动校验返回的结果覆盖。
解决方案1:使用Field的render属性实现(推荐,可直接复用Field的校验能力)
直接用Field的children render函数,把Formik提供的属性直接绑定到DatePicker上,不需要额外维护本地状态:
import DatePicker from "react-datepicker"; import "react-datepicker/dist/react-datepicker.css"; import { Field } from "formik"; // 组件内使用 <Field name='datepicker' validate={validate}> {({ field, form: { touched, errors, setFieldTouched, setFieldValue } }) => ( <> <DatePicker id="datepicker" name={field.name} selected={field.value} onChange={(val) => { setFieldTouched('datepicker', true, false); // 第三个参数为true表示修改值后自动触发校验 setFieldValue('datepicker', val, true); }} required /> {/* 直接绑定Formik生成的错误提示 */} {touched.datepicker && errors.datepicker && <div className="error">{errors.datepicker}</div>} </> )} </Field>
该方案不需要自行维护date状态和同步useEffect,所有状态统一存储在Formik中,不会出现输入流程被打断的问题,校验逻辑走Field的validate属性即可自动生成errors,不需要手动调用setFieldError。
解决方案2:不使用Field,通过useFormikContext绑定
如果不想依赖Field组件,可以直接从Formik上下文获取所有实例方法,灵活度更高:
import DatePicker from "react-datepicker"; import "react-datepicker/dist/react-datepicker.css"; import { useFormikContext } from "formik"; const DatepickerField = ({ name, validate }) => { const { values, touched, errors, setFieldTouched, setFieldValue, setFieldError } = useFormikContext(); // 自定义校验逻辑示例 const customValidate = (val) => { const err = validate(val); if (err) { setFieldError(name, err); } }; return ( <> <DatePicker id={name} name={name} selected={values[name]} onChange={(val) => { setFieldTouched(name, true, false); setFieldValue(name, val, true); // 可自行选择时机调用自定义校验 // customValidate(val); }} required /> {touched[name] && errors[name] && <div className="error">{errors[name]}</div>} </> ) } // 父组件直接调用即可 <DatepickerField name="datepicker" validate={validate} />
补充说明
如果需要强制手动设置错误信息,需要关闭setFieldValue的自动校验触发,避免自动校验结果覆盖手动设置的错误:
// 第三个参数设为false,修改值后不触发自动校验 setFieldValue('datepicker', val, false); setFieldError('datepicker', '自定义错误提示内容');
内容的提问来源于stack exchange,提问作者Nukodi
相关产品推荐
相关产品推荐

