You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 04:47:08