Formik Yup条件验证问题:密码非空时校验确认密码
个人资料表单验证问题及修复
我有一个个人资料设置表单,所有字段均为非必填项。其中包含password和confirmNewPassword字段,需求是:当password字段输入内容时,confirmNewPassword字段变为必填且需与password匹配。
原验证Schema
const validationSchema = Yup.object({ firstName: Yup.string() .min(3, 'Firstname should be longer than 2 symbols') .max(255, 'Firstname should be shorter than 255 symbols') .nullable(true) .transform((_, value: string) => { return value === '' ? null : value }), lastName: Yup.string() .min(3, 'Lastname should be longer than 2 symbols') .max(255, 'Lastname should be shorter than 255 symbols') .nullable(true) .transform((_, value: string) => { return value === '' ? null : value }), email: Yup.string() .email('Invalid email format') .nullable(true) .transform((_, value: string) => { return value === '' ? null : value }), phone: Yup.string() .matches(/^[0-9]+$/, 'Must be only digits') .min(4, 'Phone should be longer than 3 symbols') .max(12, 'Phone should be shorter than 12 symbols') .nullable(true) .transform((_, value: string) => { return value === '' ? null : value }), password: Yup.string() .min(4, 'Password should be longer than 3 symbols') .max(30, 'Password should be shorter than 30 symbols') .matches(/^[a-zA-Z0-9-]+$/, 'Only English letters and numbers') .nullable(true) .transform((_, value: string) => { return value === '' ? null : value }), confirmNewPassword: Yup.string() .when('password', { is: (password: string) => password !== '', then: Yup.string() .oneOf([Yup.ref('password'), null], 'Passwords must match') .required('Field required, while changing password'), }) .nullable() .transform((_, value: string) => { return value === '' ? null : value }), })
遇到的问题
未填写password和confirmNewPassword时,无法修改其他任何字段。
问题根源
问题出在confirmNewPassword的when条件判断上:
由于password字段的transform会把空字符串转成null,当用户没填密码时,password的值是null。此时is: (password) => password !== ''这个条件会成立(因为null !== ''为真),导致then分支的required验证被触发,强制要求confirmNewPassword必填,进而整个表单验证失败,无法提交其他字段的修改。
修复后的Schema
const validationSchema = Yup.object({ firstName: Yup.string() .min(3, '名字长度需大于2个字符') .max(255, '名字长度需小于255个字符') .nullable(true) .transform((_, value: string) => { return value === '' ? null : value }), lastName: Yup.string() .min(3, '姓氏长度需大于2个字符') .max(255, '姓氏长度需小于255个字符') .nullable(true) .transform((_, value: string) => { return value === '' ? null : value }), email: Yup.string() .email('邮箱格式无效') .nullable(true) .transform((_, value: string) => { return value === '' ? null : value }), phone: Yup.string() .matches(/^[0-9]+$/, '只能输入数字') .min(4, '手机号长度需大于3个字符') .max(12, '手机号长度需小于12个字符') .nullable(true) .transform((_, value: string) => { return value === '' ? null : value }), password: Yup.string() .min(4, '密码长度需大于3个字符') .max(30, '密码长度需小于30个字符') .matches(/^[a-zA-Z0-9-]+$/, '只能包含英文字母、数字和短横线') .nullable(true) .transform((_, value: string) => { return value === '' ? null : value }), confirmNewPassword: Yup.string() .when('password', { // 仅当password有实际输入值时触发验证 is: (password: string | null) => password != null && password.trim() !== '', then: (schema) => schema .oneOf([Yup.ref('password')], '两次输入的密码不匹配') .required('修改密码时此字段为必填'), // 无密码输入时,允许confirmNewPassword为空 otherwise: (schema) => schema.nullable() }) .transform((_, value: string) => { return value === '' ? null : value }), })
修复说明
- 修正条件判断:把
is的判断逻辑改成password != null && password.trim() !== '',确保只有用户实际输入了密码内容时,才触发confirmNewPassword的必填验证。 - 优化密码匹配逻辑:去掉
oneOf里的null选项,因为当密码有输入时,确认密码必须是匹配的非空值。 - 明确无密码时的规则:添加
otherwise分支,指定当没有输入密码时,confirmNewPassword可以为null,避免默认逻辑导致的验证冲突。 - 替换中文提示:将所有错误提示改为中文,更贴合使用场景。
修改后,不填密码时其他字段可正常修改提交;输入密码时,确认密码会被强制要求必填且与密码匹配。
内容的提问来源于stack exchange,提问作者Ivan Ivaniuk
相关产品推荐
相关产品推荐

