如何用Yup实现带掩码字段的可选验证?
问题:Yup实现字段非空时才验证,为空则跳过
我用Yup做表单字段验证,其中一个带自定义掩码的字段salesmanId需要仅在填写内容时验证格式,为空则完全不验证。目前customerId是必填字段,验证正常,但salesmanId我已经去掉了required(),还是达不到预期效果。
相关代码如下:
import { formMessages } from '@constants'; import { isValid as isValidCpf } from '@fnando/cpf'; import * as Yup from 'yup'; export const validationSchema = Yup.object({ customerId: Yup.string() .required(formMessages.requiredIdValidationMessage) .test( 'customerId', formMessages.invalidIdValidationMessage, (value = '') => { return isValidCpf(value!); }, ), // THIS ONE HAS TO BE OPTIONAL salesmanId: Yup.string().test( 'salesmanId', formMessages.invalidIdValidationMessage, (value = '') => { return isValidCpf(value!); }, ), });
<TextField autoComplete="none" id="salesman-cpf" name="salesmanCpf" type="text" label="CPF Vendedor" value={values.salesmanCpf} onChange={(event) => { if ( isValidCpfInput(event.target.value) && event.target.value !== '' ) { handleChange(event); } }} onPaste={(event) => handlePaste('salesmanCpf', 11, event, setFieldValue) } helperText={touched.salesmanCpf ? errors.salesmanCpf : ''} error={touched.salesmanCpf && Boolean(errors.salesmanCpf)} InputProps={{ inputComponent: MaskedField, inputProps: { replacement: { _: /\d/ }, mask: '___.___.___-__', 'data-testid': 'input-salesman-cpf', autoComplete: 'none', }, }} InputLabelProps={{ shrink: true }} FormHelperTextProps={ { 'data-testid': 'input-salesmancpf-helper', } as InputHTMLAttributes<HTMLInputElement> } fullWidth />
解决方案
核心要解决两个点:一是在自定义验证逻辑里判断空值并跳过校验,二是保证表单字段名和Yup schema的字段名一致。
1. 修正Yup验证规则
给salesmanId的test方法加个前置判断:如果值为空(或仅含空白字符),直接返回true通过验证;非空时再执行CPF格式校验:
import { formMessages } from '@constants'; import { isValid as isValidCpf } from '@fnando/cpf'; import * as Yup from 'yup'; export const validationSchema = Yup.object({ customerId: Yup.string() .required(formMessages.requiredIdValidationMessage) .test( 'customerId', formMessages.invalidIdValidationMessage, (value = '') => { return isValidCpf(value!); }, ), salesmanId: Yup.string().test( 'salesmanId', formMessages.invalidIdValidationMessage, (value = '') => { // 为空时直接通过验证 if (!value.trim()) return true; // 非空时校验CPF格式 return isValidCpf(value!); }, ), });
2. 统一表单字段名
你的Yup schema里字段是salesmanId,但TextField的name属性是salesmanCpf,这会导致验证无法关联到正确字段,必须统一:
<TextField autoComplete="none" id="salesman-cpf" name="salesmanId" // 改为和schema一致的字段名 type="text" label="CPF Vendedor" value={values.salesmanId} // 对应的值同步修改 onChange={(event) => { if ( isValidCpfInput(event.target.value) && event.target.value !== '' ) { handleChange(event); } }} onPaste={(event) => handlePaste('salesmanId', 11, event, setFieldValue) // 字段名同步修改 } helperText={touched.salesmanId ? errors.salesmanId : ''} // 同步修改 error={touched.salesmanId && Boolean(errors.salesmanId)} // 同步修改 InputProps={{ inputComponent: MaskedField, inputProps: { replacement: { _: /\d/ }, mask: '___.___.___-__', 'data-testid': 'input-salesman-cpf', autoComplete: 'none', }, }} InputLabelProps={{ shrink: true }} FormHelperTextProps={ { 'data-testid': 'input-salesmancpf-helper', } as InputHTMLAttributes<HTMLInputElement> } fullWidth />
额外处理:掩码组件的空值问题
如果你的掩码组件会给空值自动填充下划线(比如___.___.___-__),需要先清理非数字字符再判断:
salesmanId: Yup.string().test( 'salesmanId', formMessages.invalidIdValidationMessage, (value = '') => { // 去掉掩码中的非数字字符 const cleanedValue = value.replace(/\D/g, ''); // 清理后为空则通过验证 if (!cleanedValue) return true; return isValidCpf(cleanedValue); }, ),
内容的提问来源于stack exchange,提问作者jvbs
相关产品推荐
相关产品推荐

