[英]How do I handle validations per-field in a Formik FieldArray?
如果有 3 名参与者,所有 3 名参与者将有相同的一组问题,但每个问题都有一个“已申请的票证”列表,该列表将决定问题是否显示给特定参与者。
我需要知道
yup
关闭特定字段的验证(据我所知,这是不可能的,因为所有字段验证都是在开始时提供的)yup
由于所有内置验证检查,我仍然希望能够使用是的我有一个表单,它要求我有条件地在 Formik 中呈现特定字段,但如果该字段不存在,还要从中删除验证。
此表单的具体问题是它包含多个通过 Formiks FieldArray 处理的“子表单” 。 它的结构看起来像这样。
这些子表单中的字段可以打开和关闭,但这不适用于验证模式。
到目前为止,我已经尝试使用yup.when()
function 来有条件地渲染它,它会执行预期的操作,但它会为表单中的所有元素删除它。 片段附在这里,
/**
* Given a list of applied tickets, turn off the validation whenever the "id" property
* does not exist in the appliedTickets array
*/
if (field.appliedTickets.length > 0) {
schema = schema.when("id", {
is: (value: any) => field.appliedTickets.every((t) => t !== value),
then: (s) => s.notRequired(),
otherwise: (s) => s,
});
}
const personalDetailsSchema = useMemo(() => {
const ticketSchema = {
id: string().strip(),
firstName: string()
.trim()
.required(getErrorMessage("firstName.label", { required: true })),
lastName: string()
.trim()
.required(getErrorMessage("lastName.label", { required: true })),
gender: mixed<Gender>()
.required(getErrorMessage("gender.label", { required: true }))
.defined()
.default(Gender.male),
email: string()
.email("Must be a valid email")
.required(getErrorMessage("email.label", { required: true })),
dateOfBirth: date()
.required(getErrorMessage("dateOfBirth.label", { required: true }))
.typeError("A valid date is required")
.nullable()
.default(null),
nationality: string()
.oneOf<Country["abbr"]>(
allCountries.map((c) => c.abbr),
"An available nationality is required"
)
.required(getErrorMessage("nationality.label", { required: true }))
.defined(),
extraInfo: customQuestionSchema
? object({ id: string().strip(), ...customQuestionSchema })
: mixed().notRequired(),
};
const personalDetailsSchema = object({
tickets: array(object(ticketSchema)),
});
return personalDetailsSchema;
}, [getErrorMessage, customQuestionSchema]);
这里的每个自定义问题都有一个包含 isPredefinedType 的形状。 基于此,有两个函数根据自定义问题提供的数据动态生成schema的rest。
const customQuestionSchema: Nullable<Record<string, AnySchema>> =
useMemo(() => {
if (!customQuestions) {
return;
}
const jointSchema = customQuestions?.reduce((previous, current) => {
if (current.isPreDefinedType) {
const presetQuestionSchema = getPredefinedSchemaFor(current);
if (presetQuestionSchema) {
return Object.assign(previous, {
[presetQuestionSchema.name]: presetQuestionSchema.objectSchema,
});
}
}
const { name, objectSchema } = getCustomSchemaFor(current);
return Object.assign(previous, {
[name]: objectSchema,
});
}, {});
return jointSchema;
}, [customQuestions, getCustomSchemaFor, getPredefinedSchemaFor]);
const personalDetailsSchema = object({
tickets: array(object(ticketSchema)),
})
最终,我决定切换到字段级别验证,同时仍然使用 yup 创建自定义和动态模式。
我手动调用了Schema.validate
function 并将结果分配给 Formik Field
组件的validate
属性,如下所示,
const validateField = (name: string, question: CustomQuestion, value: any) => {
const validationResult = validate(question, value);
if (validationResult.isError) setFieldError(name, validationResult.message);
return validationResult.message;
};
}
<FormikDropdown
{...defaultProps}
values={question.values}
onValidate={(value) => validateField(questionFormFieldName, question, value)}
/>
要从表单中完全删除该字段,我必须在我创建的 FormikDropdown 组件中的useEffect
挂钩的清理 function 中调用setValue
、 setError
和setTouched
,
useEffect(() => {
return () => {
setValue(undefined);
setError(undefined);
setTouched(false);
};
}, []);
这最终使我可以在验证过程中省略特定字段,如果在过程中间删除它们,也可以将它们删除,这样任何剩余的数据都不会存在。
但是,我还没有完全测试过任何边缘情况,它可能仍然存在错误,但就我的初始测试而言,该方法按预期工作
不过要注意一件小事。 我已经从我的依赖数组中省略了 3 个函数。 原因是由于某种原因,包括它们似乎会导致无限的重新渲染循环。 它正在 Formik 在Github 中的讨论线程上进行讨论,在撰写本文时似乎仍然没有官方修复。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.