You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Yup适配dispatch动作:updateUser时将密码字段设为可选校验

需求说明

当dispatch触发updateUser动作分发时,仅将password、confirmPassword两个字段的校验设置为可选,其余字段校验规则保持不变

问题描述

如何针对特定的用户动作停用指定字段的校验规则?
当前使用的Yup校验Schema如下:

const RegisterSchema = Yup.object().shape({
    code: Yup.string()
        .required("Employee Code is required!")
        .max(8, "Employee Code should not exceed 8 characters")
        .matches(/^[0-9]+$/, "Only number are allowed for this field"),
    firstName: Yup.string()
        .required("First name is required!")
        .max(16, "First name should not exceed 16 characters")
        .required("First name is required")
        .matches(/^[aA-zZ]+$/, "Invalid First name"),
    lastName: Yup.string()
        .required("Last name is required!")
        .max(16, "Last name should not exceed 16 characters")
        .required("Last name is required")
        .matches(/^[aA-zZ]+$/, "Invalid Last name"),
    userName: Yup.string()
        .required("User name is required")
        .matches(/^[^\s]+(\s+[^\s]+)*$/, "Invalid User name")
        .max(16, "User name should not exceed 16 characters"),
    address: Yup.string()
        .required("Address is required")
        .matches(/^[^\s]+(\s+[^\s]+)*$/, "Invalid Address"),
    role: Yup.mixed().required("Role is required!"),
    desigination: Yup.mixed().required("Desigination is required!"),
    department: Yup.mixed().required("Department is required!"),
    date: Yup.mixed().required("Date is required"),
    city: Yup.mixed().required("City name is required"),
    country: Yup.mixed().required("Country name is required"),
    phone: Yup.string()
        .matches(
            /^(?:(?:\(?(?:00|\+)([1-4]\d\d|[1-9]\d?)\)?)?[\-\.\\/]?)?((?:\(?\d{1,}\)?[\-\.\\/]?){0,})(?:[\-\.\\/]?(?:#|ext\.?|extension|x)[\-\.\\/]?(\d+))?$/i,
            "Phone number should be numeric only!"
        )
        .required("Phone number is required")
        .min(10, "Phone number must be at least 10 characters")
        .max(10, "Phone number must be at least 10 characters"),

    email: Yup.string()
        .email("Email must be a valid email address")
        .required("Email is required"),
    password: Yup.string().when("check", {
        is: (check) => true,
        then: Yup.string()
            .required("Password is required")
            .matches(
                /^(?=(?:.*[A-Z].*){1})(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/,
                "Must Contain 8 Characters,  One Uppercase, One Lowercase, One Number and one special case Character"
            ),
    }),
    confirmPassword: Yup.string().when("check", {
        is: (check) => true,
        then: Yup.string()
            .required("Confirm Password is required")
            .oneOf([Yup.ref("password"), null], "Passwords must match"),
    }),
});

已尝试用Yup的when条件方法实现效果,但该方法仅能依赖表单内的布尔属性切换校验规则,无法适配外部dispatch动作的判断逻辑。当前Formik配置代码如下:

const formik = useFormik({
    enableReinitialize: true,
    initialValues: userDetail,
    validationSchema: RegisterSchema,
    onSubmit: async (values) => {
        let roleName: any = [];
        values.role.forEach((role) => {
            roleName.push(role.label);
        });

        let x: any = location.state;
        if (location.state !== undefined) {
            await dispatch(
                updateUser({
                    id: x.userId,
                    firstName: values.firstName,
                    lastName: values.lastName,
                    userName: values.userName,
                    desigination: values.desigination.value,
                    department: values.department.value,
                    role: roleName ? roleName : roles[0],
                    date: values.date,
                    address: values.address,
                    city: values.city,
                    country: values.country,
                    email: values.email,
                    password: values.password,
                    confirmPassword: values.confirmPassword,
                    phone: values.phone,
                    code: values.code,
                })
            );
        } else {
            await dispatch(
                addUser({
                    firstName: values.firstName,
                    lastName: values.lastName,
                    userName: values.userName,
                    desigination: values.desigination.value,
                    department: values.department.value,
                    role: roleName ? roleName : roles[0],
                    date: values.date,
                    address: values.address,
                    city: values.city,
                    country: values.country,
                    email: values.email,
                    password: values.password,
                    confirmPassword: values.confirmPassword,
                    phone: values.phone,
                    code: values.code,
                })
            );
        }
    },
});
实现方案

核心逻辑是把「当前是编辑更新用户还是新增用户」的场景标识注入表单初始值,让Yup的when方法可以直接读取标识切换校验规则,不需要依赖外部dispatch状态。

  • 第一步:提前判断当前场景,代码中location.state !== undefined即为触发updateUser的编辑场景,其余为新增用户场景。
  • 第二步:修改Yup校验Schema,保留其他字段原有校验规则不变,仅调整password、confirmPassword两个字段的when判断逻辑,新增场景下保持原有必填+强规则校验,编辑场景下两个字段设为可选,若用户主动填写了密码,再触发格式、一致性校验。
  • 第三步:调整提交逻辑,编辑场景下如果用户未填写密码,提交参数中不传递密码字段,避免空值覆盖用户原有密码。

修改后的校验Schema代码:

const RegisterSchema = Yup.object().shape({
    code: Yup.string()
        .required("Employee Code is required!")
        .max(8, "Employee Code should not exceed 8 characters")
        .matches(/^[0-9]+$/, "Only number are allowed for this field"),
    firstName: Yup.string()
        .required("First name is required!")
        .max(16, "First name should not exceed 16 characters")
        .matches(/^[aA-zZ]+$/, "Invalid First name"),
    lastName: Yup.string()
        .required("Last name is required!")
        .max(16, "Last name should not exceed 16 characters")
        .matches(/^[aA-zZ]+$/, "Invalid Last name"),
    userName: Yup.string()
        .required("User name is required")
        .matches(/^[^\s]+(\s+[^\s]+)*$/, "Invalid User name")
        .max(16, "User name should not exceed 16 characters"),
    address: Yup.string()
        .required("Address is required")
        .matches(/^[^\s]+(\s+[^\s]+)*$/, "Invalid Address"),
    role: Yup.mixed().required("Role is required!"),
    desigination: Yup.mixed().required("Desigination is required!"),
    department: Yup.mixed().required("Department is required!"),
    date: Yup.mixed().required("Date is required"),
    city: Yup.mixed().required("City name is required"),
    country: Yup.mixed().required("Country name is required"),
    phone: Yup.string()
        .matches(
            /^(?:(?:\(?(?:00|\+)([1-4]\d\d|[1-9]\d?)\)?)?[\-\.\\/]?)?((?:\(?\d{1,}\)?[\-\.\\/]?){0,})(?:[\-\.\\/]?(?:#|ext\.?|extension|x)[\-\.\\/]?(\d+))?$/i,
            "Phone number should be numeric only!"
        )
        .required("Phone number is required")
        .min(10, "Phone number must be at least 10 characters")
        .max(10, "Phone number must be at least 10 characters"),
    email: Yup.string()
        .email("Email must be a valid email address")
        .required("Email is required"),
    password: Yup.string().when("isEditMode", {
        is: false,
        then: Yup.string()
            .required("Password is required")
            .matches(
                /^(?=(?:.*[A-Z].*){1})(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/,
                "Must Contain 8 Characters,  One Uppercase, One Lowercase, One Number and one special case Character"
            ),
        otherwise: Yup.string().notRequired().matches(
            /^(?=(?:.*[A-Z].*){1})(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/,
            "Must Contain 8 Characters,  One Uppercase, One Lowercase, One Number and one special case Character"
        )
    }),
    confirmPassword: Yup.string().when("isEditMode", {
        is: false,
        then: Yup.string()
            .required("Confirm Password is required")
            .oneOf([Yup.ref("password"), null], "Passwords must match"),
        otherwise: Yup.string().notRequired().when("password", {
            is: (pwdVal) => !!pwdVal,
            then: Yup.string().oneOf([Yup.ref("password"), null], "Passwords must match")
        })
    }),
    isEditMode: Yup.boolean()
});

修改后的Formik配置代码:

const isEditMode = location.state !== undefined;
const formik = useFormik({
    enableReinitialize: true,
    initialValues: {
        ...userDetail,
        isEditMode
    },
    validationSchema: RegisterSchema,
    onSubmit: async (values) => {
        let roleName: any = [];
        values.role.forEach((role) => {
            roleName.push(role.label);
        });

        const baseParams = {
            firstName: values.firstName,
            lastName: values.lastName,
            userName: values.userName,
            desigination: values.desigination.value,
            department: values.department.value,
            role: roleName.length ? roleName : roles[0],
            date: values.date,
            address: values.address,
            city: values.city,
            country: values.country,
            email: values.email,
            phone: values.phone,
            code: values.code,
        }

        if (values.password) {
            baseParams.password = values.password;
            baseParams.confirmPassword = values.confirmPassword;
        }

        if (isEditMode) {
            await dispatch(
                updateUser({
                    id: location.state.userId,
                    ...baseParams
                })
            );
        } else {
            await dispatch(
                addUser({
                    ...baseParams,
                    password: values.password,
                    confirmPassword: values.confirmPassword,
                })
            );
        }
    },
});

说明:新增的isEditMode字段仅做校验逻辑判断使用,不需要在页面中渲染对应的表单项,不会影响原有页面结构。

内容的提问来源于stack exchange,提问作者Wajid Ali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 19:21:31