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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 03:35:32