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
相关产品推荐
相关产品推荐

