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

Formik与Yup校验问题:字符串及日期数组字段验证方案咨询

Yup表单校验问题解决

问题回顾

我有两个字段:字符串字段prod和日期数组字段date,用Yup校验时碰到两个问题:

  1. oneOf和自定义test都没法校验prod是否在指定字符串数组里;
  2. 日期字段为空时,prod的“required”错误会被触发。

需求明确:

  • prod:非空且值必须在给定字符串数组内;
  • date:非空且必须包含两个日期。

当前使用的代码:

const myArray = useMemo(() => someData.map(({ value }) => value), [someData]); // string[]

const schema = yup.object({
  prod: yup
   .string()
   .ensure()
   .oneOf(myArray, 'not in list') // 不生效
   .required('add prod') // 其他字段为空也触发这个提示 :(
   .test( // 不生效
     'prod',
     'not in list 2',
     () => !!myArray.find((prod) => prod === myForm.values.prod)
   ),
   date: yup.array().required().min(1, 'add date').length(2, 'add second date'),
});

问题原因

  1. oneOf和自定义test失效:
    • 自定义test直接依赖外部的myForm.values.prod,不符合Yup校验逻辑——校验函数应使用传入的value参数,而非外部表单状态,否则会导致校验触发时机或结果错误。
    • .ensure()会把undefined转为空字符串,若myArray不含空字符串,oneOf会报错,但后续.required()的逻辑会造成校验流程混乱。
  2. 跨字段触发required提示:
    • 日期数组校验写法不严谨,未明确元素类型;且表单整体校验时,若prod初始值为空,任何字段校验失败都会触发prod的required校验(表单库会重新校验所有字段)。

修正后的Schema

const myArray = useMemo(() => someData.map(({ value }) => value), [someData]);

const schema = yup.object({
  prod: yup
    .string()
    .required('请选择产品') // 先校验必填,再校验范围
    .oneOf(myArray, '所选产品不在允许列表中'),
  date: yup
    .array()
    .of(yup.date().required('日期不能为空')) // 明确数组元素是有效日期
    .required('请选择两个日期')
    .length(2, '必须选择两个日期'),
});

额外说明

  • 去掉.ensure():required()会直接捕获undefined或空字符串,无需转义,让校验逻辑更清晰。
  • 自定义test替代方案(如果需要):
    .test(
      'prod-in-list',
      '所选产品不在允许列表中',
      (value) => myArray.includes(value)
    )
    
    必须使用校验函数的value参数,避免依赖外部状态。
  • 日期数组校验优化:.length(2)已经确保数组有两个元素,无需额外min(1),加上.of(yup.date())能保证每个元素都是有效的日期类型。

内容的提问来源于stack exchange,提问作者Will Black

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 19:25:23