You need to enable JavaScript to run this app. FieldArray场景下如何为元素唯一性Yup校验配置正确错误提示?
优惠活动
大模型
产品
解决方案
定价
更多

FieldArray场景下如何为元素唯一性Yup校验配置正确错误提示?

FieldArray 邮箱校验的错误展示修复方案

问题根源

  • 你配置的唯一性校验属于数组根级别校验,校验失败时,错误信息默认会挂载在整个数组字段(对应路径${name})下
  • 循环内的ErrorMessage组件绑定的是单个数组元素的路径${name}.${i},当当前元素没有触发自身的格式校验错误时,Formik的错误解析逻辑会沿着路径层级向上查找,最终拿到数组根路径上的字符串形式的错误提示
  • 由于组件会按索引i对获取到的内容做取值操作,字符串是可按索引访问的类数组结构,最终就出现了每个循环位置逐字拆分显示错误提示的异常效果

可落地方案

方案1:分层渲染错误(改动量最小)

不需要修改现有Yup校验逻辑,只要调整渲染结构,把数组级错误和元素级错误的渲染位置完全分开,避免跨路径取值:

  • 循环内部保留原有的单元素错误展示逻辑,专门负责渲染单个邮箱的格式校验提示
  • 在循环结构的外部、数组字段的容器位置,单独增加一个绑定了数组根路径的ErrorMessage组件,专门渲染唯一性校验的提示

调整后的渲染代码如下:

render={({ form, push, remove }) => (
  <div>
    {/* 单独渲染数组根级的唯一性错误,放在循环外侧 */}
    <ErrorMessage
      name={name}
      render={msg => (
        <FormFeedback style={{ display: "block" }}>
          {msg}
        </FormFeedback>
      )}
    />

    {/* 原有循环逻辑保持不变 */}
    {range(0, form.values[name].length).map(i => (
      <div className={styles.inputContainer} key={i}>
        <div>
          <InputComponent name={`${name}.${i}`} {...props} />
          <Button color="danger" onClick={() => remove(i)}>
            <FaTimes />
          </Button>
        </div>
        {/* 循环内的ErrorMessage仅负责渲染单元素的格式错误 */}
        <ErrorMessage
          name={`${name}.${i}`}
          render={msg => (
            <FormFeedback style={{ display: "block" }}>
              {msg}
            </FormFeedback>
          )}
        />
      </div>
    ))}

    <Button
      color="info"
      onClick={() => push("")}
      disabled={disabled || form.values[name].slice(-1)[0] === ""}
    >
      <FaPlus />
    </Button>
  </div>
)}

该方案适配成本最低,完全兼容现有校验规则,只要明确划分两类错误的渲染边界,就能彻底解决逐字拆分显示的问题。缺点是唯一性提示统一展示在数组区域顶部,无法精准定位到具体重复的输入框位置。

方案2:调整Yup校验逻辑,实现精准错误定位

如果需要把唯一性校验的提示精准展示在重复项的输入框下方,只需要调整原有Yup规则的错误挂载逻辑,不要把错误挂在数组根路径,而是通过createError把错误绑定到具体重复元素的对应路径上,让循环内的错误组件可以正常读取到对应内容。

调整后的Yup校验规则:

export const emails = yup
  .array(
    yup
      .string()
      .matches(/^\S+@\S+$/, {
        message: "Must be a valid email id"
      })
  )
  .test("email-unique", "Email ids need to be unique", function(values) {
    // 过滤空值,空值的格式校验由子项规则处理,不参与唯一性判断
    const nonEmptyValues = values.filter(Boolean);
    // 无重复值时直接通过校验
    if (new Set(nonEmptyValues).size === nonEmptyValues.length) return true;

    // 统计每个邮箱值对应的所有索引位置
    const valueIndexMap = {};
    values.forEach((value, index) => {
      if (!value) return;
      if (!valueIndexMap[value]) valueIndexMap[value] = [];
      valueIndexMap[value].push(index);
    });

    // 收集所有重复值对应的索引
    const duplicatedIndexes = Object.values(valueIndexMap)
      .filter(indexList => indexList.length > 1)
      .flat();

    // 将错误精准挂载到对应重复元素的路径上,而非数组根路径
    return this.createError({
      path: `${this.path}.${duplicatedIndexes[0]}`,
      message: "Email ids need to be unique"
    })
  });

调整后,唯一性错误会直接挂载到对应元素的路径下,循环内原有的错误展示逻辑不需要做任何修改,就可以同时兼容单元素格式校验、数组级唯一性校验的错误展示,还能把错误提示精准定位到重复的输入框位置。
注意:test方法的回调必须使用普通函数,不能使用箭头函数,否则无法获取Yup的校验上下文,会导致createError等方法调用失败。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.29 17:27:03