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

Formik如何为动态渲染的多组同字段表单添加Validation Schema校验规则

Formik 动态表单字段校验实现方案

方案1:使用 Yup 定义结构化校验规则(推荐)

这是最适配你场景的实现方案,Yup 内置数组类型校验能力,可以直接对otherPersons数组的所有子项设置统一校验逻辑,不需要手动根据动态生成的表单数逐个写规则。
如果还没安装 Yup 先执行安装:

npm install yup
# 或者
yarn add yup

然后在 Formik 初始化配置中定义validationSchema:

import * as Yup from 'yup';

const formik = useFormik({
  initialValues: {
    otherPersons: calcInitialValues(journey),
    // 其他表单初始值...
  },
  validationSchema: Yup.object({
    // 其他字段的校验规则...
    otherPersons: Yup.array().of(
      Yup.object({
        name: Yup.string().required('请填写姓名'),
        email: Yup.string().email('邮箱格式不正确').required('请填写邮箱'),
        address: Yup.string().required('请填写地址'),
        // 可扩展其他字段校验规则
      })
    )
  }),
  onSubmit: values => {
    // 表单提交逻辑
  }
})

方案2:手动编写 validate 函数

如果不想引入 Yup 依赖,也可以通过 Formik 原生的 validate 函数手动实现校验:

const formik = useFormik({
  initialValues: {
    otherPersons: calcInitialValues(journey),
    // 其他表单初始值...
  },
  validate: values => {
    const errors = { otherPersons: [] };
    
    values.otherPersons.forEach((person, index) => {
      const personErrors = {};
      if (!person.name) personErrors.name = '请填写姓名';
      if (!person.email) {
        personErrors.email = '请填写邮箱';
      } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(person.email)) {
        personErrors.email = '邮箱格式不正确';
      }
      if (!person.address) personErrors.address = '请填写地址';
      
      if (Object.keys(personErrors).length) {
        errors.otherPersons[index] = personErrors;
      }
    })
    return errors;
  },
  onSubmit: values => {
    // 表单提交逻辑
  }
})

组件内错误提示适配

修改OtherPersons组件,接入touched和errors判断逻辑,实现触碰后展示错误的效果:

export default function OtherPersons({formik, index}) {
  // 快捷获取当前表单项的触碰状态和错误信息
  const currentTouched = formik.touched.otherPersons?.[index];
  const currentErrors = formik.errors.otherPersons?.[index];

  return (
    <div className="form-item-wrapper">
      <Input
        size="large"
        className="input-box full-width-input"
        {...formik.getFieldProps(`otherPersons[${index}].name`)}
      />
      {/* 姓名错误提示 */}
      {currentTouched?.name && currentErrors?.name && <span className="error-tip">{currentErrors.name}</span>}

      {/* 邮箱输入框+错误提示 */}
      <Input
        size="large"
        className="input-box full-width-input"
        {...formik.getFieldProps(`otherPersons[${index}].email`)}
      />
      {currentTouched?.email && currentErrors?.email && <span className="error-tip">{currentErrors.email}</span>}

      {/* 地址输入框+错误提示 */}
      <Input
        size="large"
        className="input-box full-width-input"
        {...formik.getFieldProps(`otherPersons[${index}].address`)}
      />
      {currentTouched?.address && currentErrors?.address && <span className="error-tip">{currentErrors.address}</span>}
    </div>
  )
}

补充优化建议

你当前的calcInitialValues函数存在逻辑问题:循环从i=1开始且判断条件为i < insuredPersonsCount,如果 API 返回3,最终只会生成2个表单项,可优化为更简洁的写法:

export const calcInitialValues = journey => {
  if (!journey) return [];
  return Array.from({ length: journey.insuredPersons }, () => ({
    address: '',
    email: '',
    name: '',
  }))
}

内容的提问来源于stack exchange,提问作者RRR uzumaki

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 04:00:02