如何在React+Formik中使用Yup异步验证并避免先显示必填错误
解决Formik+Yup条码表单异步验证时必填错误误显示问题
问题描述
我使用Formik和Yup构建React表单,实现条码输入列表功能,需通过API异步验证条码是否存在。当前代码运行时出现异常:添加多个输入框并输入内容后,会先显示“Barcode is required”的必填错误提示,但此时输入框已有内容。我希望仅在输入框为空时显示必填提示,输入内容后只展示异步验证的结果。
现有代码
import { Formik, Field, FieldArray, Form, ErrorMessage } from "formik"; import * as Yup from "yup"; interface BarcodeFormValues { barcodes: { code: string }[]; } const validationSchema = Yup.object({ barcodes: Yup.array().of( Yup.object().shape({ code: Yup.string() .required("Barcode is required") .test("checkBarcodeExists", "Barcode does not exist", async (value) => { if (!value) return false; // 模拟API延迟 await new Promise((resolve) => setTimeout(resolve, 2000)); // 模拟验证失败 return false; }), }) ), }); const initialValues: BarcodeFormValues = { barcodes: [{ code: "" }], }; const BarcodeForm = () => { return ( <Formik initialValues={initialValues} validationSchema={validationSchema} validateOnChange={true} validateOnBlur={false} onSubmit={(values) => { console.log("Submitted values:", values); }} > {({ values }) => ( <Form> <FieldArray name="barcodes"> {({ remove, push }) => ( <div> {values.barcodes.length > 0 && values.barcodes.map((barcode, index) => ( <div key={index}> <Field name={`barcodes.${index}.code`} placeholder="Enter barcode" /> <ErrorMessage name={`barcodes.${index}.code`} component="div" /> <button type="button" onClick={() => remove(index)}> Delete </button> </div> ))} <button type="button" onClick={() => push({ code: "" })}> Add New </button> </div> )} </FieldArray> <button type="submit">Submit</button> </Form> )} </Formik> ); }; export default BarcodeForm;
解决方案
1. 调整Yup验证逻辑,分离必填与异步验证
核心问题在于原有异步验证逻辑中,空值时返回false,导致必填验证和异步验证的错误状态冲突。修改验证规则,让异步验证仅在值非空时执行,空值情况完全由必填验证处理:
const validationSchema = Yup.object({ barcodes: Yup.array().of( Yup.object().shape({ code: Yup.string() .required("Barcode is required") .test( "checkBarcodeExists", "Barcode does not exist", async (value) => { // 空值时直接返回true,交由必填验证处理错误 if (!value) return true; // 模拟API异步验证 await new Promise((resolve) => setTimeout(resolve, 2000)); // 替换为实际API请求逻辑,例如: // const res = await fetch(`/api/validate-barcode?code=${value}`); // return res.ok; // 测试用返回false return false; } ), }) ), });
2. 优化错误显示逻辑(可选)
如果仍存在错误显示延迟,可通过ErrorMessage的自定义渲染逻辑,强制过滤掉非空值时的必填错误:
<ErrorMessage name={`barcodes.${index}.code`} render={(msg) => { const currentValue = values.barcodes[index].code; // 若输入框有值,且错误为必填提示,则不显示 if (currentValue && msg === "Barcode is required") { return null; } return <div>{msg}</div>; }} />
3. 添加异步验证防抖(可选)
频繁输入会触发大量异步请求,可通过防抖减少请求次数:
先安装依赖:
npm install lodash.debounce
修改Formik配置:
import debounce from 'lodash.debounce'; import { useMemo } from 'react'; // ... const BarcodeForm = () => { // 创建防抖验证函数 const debouncedValidate = useMemo(() => debounce(async (values: BarcodeFormValues) => { try { await validationSchema.validate(values, { abortEarly: false }); return {}; } catch (err) { return Yup.errorToFormErrors(err as Yup.ValidationError); } }, 500), []); return ( <Formik initialValues={initialValues} validate={debouncedValidate} validateOnChange={true} validateOnBlur={false} onSubmit={(values) => { console.log("Submitted values:", values); }} > {/* 原有表单内容 */} </Formik> ); };
内容的提问来源于stack exchange,提问作者Jenan
相关产品推荐
相关产品推荐

