如何在Formik中实现Yup自定义验证validateGroup的条件触发
解决方案:Formik中仅在提交时执行带API调用的自定义校验
核心思路是拆分校验逻辑:实时校验仅处理必填规则(避免频繁触发API),带API调用的自定义校验只在点击提交按钮时执行,同时保留原需求中「仅当title和description都不为空时才校验group有效性」的条件。
1. 调整Validation Schema
移除原Schema中绑定到实时校验的validateGroup,只保留必填规则,避免输入时触发API:
const validationSchema = Yup.object().shape({ tasks: Yup.array().of( Yup.object().shape({ title: Yup.string().required("Required"), description: Yup.string().required("Required"), group: Yup.string().required("Required") // 移除原when逻辑中的validateGroup,防止实时触发API }) ) });
2. 在提交阶段手动执行自定义校验
利用Formik的onSubmit钩子,在点击提交按钮时手动调用带API的validateGroup,并处理错误信息:
import { Formik, Form, Field, FieldArray } from 'formik'; import * as Yup from 'yup'; // 带API调用的自定义校验函数 const validateGroup = async (groupValue) => { // 示例API调用,替换为你的实际接口逻辑 const response = await fetch(`/api/validate-group?group=${groupValue}`); const isValid = await response.json(); if (!isValid) { throw new Error("Input valid group"); } }; const TaskForm = () => { return ( <Formik initialValues={{ tasks: [{ title: '', description: '', group: '' }] }} validationSchema={validationSchema} onSubmit={async (values, { setErrors, setSubmitting, validateForm }) => { // 先执行Yup的必填校验,确保基础规则通过 const baseErrors = await validateForm(); if (Object.keys(baseErrors).length > 0) { setErrors(baseErrors); setSubmitting(false); return; } // 初始化提交错误对象 const submitErrors = { tasks: [] }; let hasValidationError = false; // 遍历每个任务,执行自定义校验 for (let i = 0; i < values.tasks.length; i++) { const task = values.tasks[i]; submitErrors.tasks[i] = {}; // 满足原需求条件:title和description都不为空时才校验group if (task.title && task.description) { try { await validateGroup(task.group); } catch (err) { submitErrors.tasks[i].group = err.message; hasValidationError = true; } } } // 存在校验错误时,设置错误并终止提交 if (hasValidationError) { setErrors(submitErrors); setSubmitting(false); return; } // 校验通过,执行实际提交逻辑 console.log('提交数据:', values); // 替换为你的提交API调用 setSubmitting(false); }} > {({ errors, touched }) => ( <Form> <FieldArray name="tasks"> {({ push, remove }) => ( <> {values.tasks.map((_, index) => ( <div key={index} className="task-row"> <div> <label>Title</label> <Field name={`tasks[${index}].title`} /> {errors.tasks?.[index]?.title && touched.tasks?.[index]?.title && ( <span className="error">{errors.tasks[index].title}</span> )} </div> <div> <label>Description</label> <Field name={`tasks[${index}].description`} /> {errors.tasks?.[index]?.description && touched.tasks?.[index]?.description && ( <span className="error">{errors.tasks[index].description}</span> )} </div> <div> <label>Group</label> <Field name={`tasks[${index}].group`} /> {/* 同时显示必填错误和提交时的自定义校验错误 */} {errors.tasks?.[index]?.group && touched.tasks?.[index]?.group && ( <span className="error">{errors.tasks[index].group}</span> )} </div> <button type="button" onClick={() => remove(index)}>删除</button> </div> ))} <button type="button" onClick={() => push({ title: '', description: '', group: '' })}>添加任务</button> </> )} </FieldArray> <button type="submit">提交表单</button> </Form> )} </Formik> ); }; export default TaskForm;
关键说明
- 实时校验仅由Yup处理必填规则,不会触发任何API调用,避免接口频繁请求
- 提交时先通过
validateForm()确保所有必填项已填写,再执行带API的自定义校验 - 自定义校验仅在满足
title和description非空的条件下执行,符合原需求逻辑 - 校验错误通过
setErrors同步到Formik状态,自动在表单中展示错误提示
内容的提问来源于stack exchange,提问作者Dante1021
相关产品推荐
相关产品推荐

