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
相关产品推荐
相关产品推荐

