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> </>
三、关键修正点说明
- 字段路径必须带索引:数组字段的name要写成
${item.name}[${index}].${ch.name},比如datePairs[0].startDate,Formik才能正确跟踪数组中每个元素的字段状态。 - 空值不强制设默认值:DatePicker空值时设为
null,否则Yup会认为字段一直有值,required验证永远不会触发。 - 手动触发onBlur:调用
form.setFieldTouched更新字段的触摸状态,否则meta.touched一直为false,错误提示不会显示。 - 绑定Material UI错误状态:通过TextField的
error和helperText属性直接关联Formik的meta状态,实现字段外框变红+错误提示的效果。
内容的提问来源于stack exchange,提问作者The Old County
相关产品推荐
相关产品推荐

