Material UI DatePicker需两次点击更新验证状态问题求助
解决Material UI DatePicker与Formik/Yup联动时验证状态延迟更新的问题
问题场景
使用Material UI DatePicker结合Formik和Yup做表单验证时,遇到以下异常:
- 首次选择未来日期,错误提示显示"Required"而非预期的"Date of birth cannot be in the future",需第二次操作才会显示正确提示;
- 当前提示为未来日期错误时,修改为过去日期后,提示不会立即更新,需点击其他区域才会消失。
相关代码如下:
DatePicker组件代码
<Grid item xs={12}> <LocalizationProvider dateAdapter={AdapterDayjs}> <DatePicker label="Date of birth" format="DD/MM/YYYY" value={values.dob} sx={{ width: '50%' }} slotProps={{ textField: { id: 'dob', helperText: touched.dob ? errors.dob : "", error: touched.dob && Boolean(errors.dob), }, }} onChange={(value) => { setFieldValue('dob', value ? dayjs(value).toDate() : null); setFieldTouched('dob', true); }} /> </LocalizationProvider> </Grid>
Formik配置
const { values, errors, touched, handleBlur, handleChange, handleSubmit, setFieldValue, setFieldTouched } = useFormik({ initialValues: { name: "", dob: null, email: "", }, validationSchema: formSchema, });
Yup验证规则
export const formSchema = yup.object().shape({ name: yup.string().matches(nameRules, 'Please enter a valid name').max(40).required("Required"), dob: yup.date().max(new Date(), 'Date of birth cannot be in the future').required('Required'), email: yup.string().email("Please enter a valid email").required("Required"), });
问题原因
setFieldValue是异步更新Formik的values状态,原代码中调用setFieldValue后立刻执行setFieldTouched,此时Formik尚未完成values的更新,Yup验证的仍是旧的dob值(比如初始的null),因此第一次触发验证时会显示"Required"错误;第二次操作时values才完成更新,验证逻辑才会检查max规则,显示正确的错误提示。
解决方案
使用Formik提供的validateField方法替代直接调用setFieldTouched,该方法会自动处理字段的touched标记,并基于最新的values执行验证逻辑,确保一次操作即可更新验证状态。
步骤1:解构validateField方法
从useFormik的返回值中解构出validateField:
const { values, errors, touched, handleBlur, handleChange, handleSubmit, setFieldValue, validateField } = useFormik({ initialValues: { name: "", dob: null, email: "", }, validationSchema: formSchema, });
步骤2:修改DatePicker的onChange逻辑
将原有的setFieldTouched替换为validateField,同时设置setFieldValue的第三个参数为false(避免自动触发全局验证):
onChange={(value) => { const newDate = value ? dayjs(value).toDate() : null; // 第三个参数设为false,不自动触发全局验证 setFieldValue('dob', newDate, false); // 手动触发该字段的验证,自动标记touched并更新errors validateField('dob'); }}
可选:完善TextField的onBlur处理
为了确保失去焦点时也能正确触发验证,可以在slotProps的textField中添加onBlur绑定:
slotProps={{ textField: { id: 'dob', helperText: touched.dob ? errors.dob : "", error: touched.dob && Boolean(errors.dob), onBlur: handleBlur // 绑定Formik的handleBlur方法 }, }}
效果验证
- 首次选择未来日期:
onChange更新dob值后,validateField立刻基于新值执行验证,错误提示直接显示"Date of birth cannot be in the future"; - 修改为过去日期:
validateField重新验证,errors.dob变为null,错误提示立即消失; - 无需二次操作或点击其他区域,验证状态实时更新。
内容的提问来源于stack exchange,提问作者Miguel Ângelo Freitas
相关产品推荐
相关产品推荐

