我有带有 formik 的 React 应用程序,是的,用于构建表单。
我需要创建包含输入列表和代码的表单,并且必须有 API 的异步验证,这将确认代码是否存在。
我尝试了以下表格:
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;
// add delay
await new Promise((resolve) => setTimeout(resolve, 2000));
// fake it, that validations failed
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;
问题是,如果您添加更多输入并尝试在输入中键入某些内容,您可以看到第一条错误消息 - “需要条形码” - 但您在那里放置了一些内容,所以我不想看到此消息,然后一段时间后,您将看到异步验证的结果。
如何调整我的代码以摆脱首先看到此“必需的错误消息”并在输入确实为空的情况下仅显示此消息。
这是codesandbox中的示例:https://codesandbox.io/p/sandbox/x6fxzr
为了成功实现这一目标,我们必须定制我们的错误。因此,删除
.required
并使用 ctx
自行解决错误。
const validationSchema = Yup.object({
barcodes: Yup.array().of(
Yup.object().shape({
code: Yup.string()
//.required("Barcode is required") Remove
.test(
"checkBarcodeExists",
"Barcode does not exist",
async (value, ctx) => {
if (!value) // Solution!!
return ctx.createError({ message: "Barcode is required" });
// add delay
await new Promise((resolve) => setTimeout(resolve, 2000));
// fake it, that validations failed
return false;
}
),
})
),
});