繁体   English   中英

如何处理 Formik FieldArray 中的每个字段的验证?

[英]How do I handle validations per-field in a Formik FieldArray?

设想:

如果有 3 名参与者,所有 3 名参与者将有相同的一组问题,但每个问题都有一个“已申请的票证”列表,该列表将决定问题是否显示给特定参与者。

TLDR;

我需要知道

  1. 是否可以在运行时使用yup关闭特定字段的验证(据我所知,这是不可能的,因为所有字段验证都是在开始时提供的)
  2. 我还有什么其他的选择来完成这个验证? 我可以使用字段级别验证, 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,
    });
}

更新 1:添加了 Schema 的

主架构

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 中调用setValuesetErrorsetTouched

useEffect(() => {
    return () => {
        setValue(undefined);
        setError(undefined);
        setTouched(false);
    };
}, []);

这最终使我可以在验证过程中省略特定字段,如果在过程中间删除它们,也可以将它们删除,这样任何剩余的数据都不会存在。

但是,我还没有完全测试过任何边缘情况,它可能仍然存在错误,但就我的初始测试而言,该方法按预期工作

不过要注意一件小事。 我已经从我的依赖数组中省略了 3 个函数。 原因是由于某种原因,包括它们似乎会导致无限的重新渲染循环 它正在 Formik 在Github 中的讨论线程上进行讨论,在撰写本文时似乎仍然没有官方修复。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM