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

Formik+Material UI+Yup的FieldArray日期验证实现难题

解决Formik FieldArray中日期字段验证不生效的问题

一、修正Yup验证Schema

你的FieldArray是数组类型的表单字段,每个数组元素是包含startDate和endDate的对象,得把单个字段的验证规则整合到Yup的数组对象结构里,匹配实际的表单数据结构:

// 假设你的FieldArray字段名为"datePairs"(对应组件里的item.name)
const validationSchema = Yup.object().shape({
  datePairs: Yup.array()
    .of(
      Yup.object().shape({
        startDate: Yup.date()
          .default(() => new Date())
          .required("开始日期必填")
          .nullable()
          .typeError("请选择有效的日期")
          .min(moment().add(1, 'weeks'), "日期不能早于1周后")
          .max(moment().add(8, 'weeks'), "日期不能晚于8周后"),
        endDate: Yup.date()
          .default(() => new Date())
          .required("结束日期必填")
          .nullable()
          .typeError("请选择有效的日期")
          .min(moment().add(1, 'weeks'), "日期不能早于1周后")
          .max(moment().add(8, 'weeks'), "日期不能晚于8周后")
          // 可选:添加结束日期必须晚于开始日期的验证
          .test(
            "end-after-start",
            "结束日期必须晚于开始日期",
            function(value) {
              const startDate = this.parent.startDate;
              return value && startDate ? value > startDate : true;
            }
          )
      })
    )
    .required("至少需要一组日期") // 限制数组不能为空
    .min(1, "至少需要一组日期");
});

二、修正FieldArray组件的核心问题

你当前的组件有3个导致验证失效的关键问题:字段路径错误、空值被默认值覆盖、未触发字段触摸状态。以下是修正后的完整代码:

<>
  <FieldArray name={item.name}>
    {({ insert, remove, push, form }) => (
      <div className="field field-array">
        {/* 初始化:如果数组为空,先添加一组默认数据 */}
        {form.values[item.name].length === 0 && push({
          startDate: moment().format('YYYY-MM-DD'),
          endDate: moment().format('YYYY-MM-DD')
        })}
        
        {/* 遍历数组中的每一组日期 */}
        {form.values[item.name].map((pair, index) => (
          <div className="row" key={index}>
            {item.fields.map((ch, inx) => (
              <span key={`x${inx}`}>
                <div className="col-x">
                  <Field name={`${item.name}[${index}].${ch.name}`}>
                    {({ field, meta }) => (
                      <>
                        <LocalizationProvider dateAdapter={AdapterDayjs}>
                          <DatePicker
                            label={ch.label}
                            disablePast={ch.disablePast}
                            disableFuture={ch.disableFuture}
                            minDate={ch.minDate}
                            maxDate={ch.maxDate}
                            // 空值时设为null,让Yup的必填验证能触发
                            value={field.value ? moment(field.value) : null}
                            onChange={(value) => {
                              form.setFieldValue(field.name, value ? value.format('YYYY-MM-DD') : null);
                              this.props.onHandle(field.name, value);
                            }}
                            // 手动触发触摸状态,确保错误提示能显示
                            onBlur={() => form.setFieldTouched(field.name, true)}
                            renderInput={(params) => (
                              <TextField
                                {...params}
                                // 绑定错误状态,实现字段外框变红
                                error={meta.touched && !!meta.error}
                                // 直接在TextField显示错误提示
                                helperText={meta.touched && meta.error ? meta.error : ''}
                              />
                            )}
                          />
                        </LocalizationProvider>
                      </>
                    )}
                  </Field>
                </div>
              </span>
            ))}
            {/* 可选:添加删除当前组的按钮 */}
            <button type="button" onClick={() => remove(index)}>删除</button>
          </div>
        ))}
        {/* 添加新增日期组的按钮 */}
        <button type="button" onClick={() => push({
          startDate: moment().format('YYYY-MM-DD'),
          endDate: moment().format('YYYY-MM-DD')
        })}>新增日期组</button>
      </div>
    )}
  </FieldArray>
</>

三、关键修正点说明

  1. 字段路径必须带索引:数组字段的name要写成${item.name}[${index}].${ch.name},比如datePairs[0].startDate,Formik才能正确跟踪数组中每个元素的字段状态。
  2. 空值不强制设默认值:DatePicker空值时设为null,否则Yup会认为字段一直有值,required验证永远不会触发。
  3. 手动触发onBlur:调用form.setFieldTouched更新字段的触摸状态,否则meta.touched一直为false,错误提示不会显示。
  4. 绑定Material UI错误状态:通过TextField的error和helperText属性直接关联Formik的meta状态,实现字段外框变红+错误提示的效果。

内容的提问来源于stack exchange,提问作者The Old County

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 16:35:22