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

React Formik动态表单:如何用Yup验证字符串数组的唯一性与必填性?

React + Formik + Yup 实现动态购票表单的数组元素唯一性与必填验证

1. 自定义Yup数组唯一性验证方法

扩展Yup添加自定义验证方法,用于检查数组元素的唯一性,同时自动忽略空字符串(避免空值被判定为重复):

import * as Yup from 'yup';

// 自定义数组唯一性验证方法
Yup.addMethod(Yup.array, 'unique', function(errorMessage) {
  return this.test('array-unique', errorMessage, function(array) {
    const nonEmptyItems = array.filter(item => item.trim() !== '');
    return new Set(nonEmptyItems).size === nonEmptyItems.length;
  });
});

2. 更新表单验证Schema

修改validationSchema,同时约束购票数量、数组必填性、元素唯一性与单个元素必填规则:

const validationSchema = Yup.object({
  numberOfTickets: Yup.number()
    .required('请选择购票数量')
    .min(1, '至少购买1张票')
    .max(10, '最多购买10张票'),
  tickets: Yup.array()
    .required('请填写所有购票人信息')
    .unique('成员值必须唯一')
    .of(
      Yup.string()
        .trim()
        .required('购票人信息不能为空')
    )
});

3. 调整onChangeTickets函数

根据用户选择的购票数量,动态调整tickets数组长度,同时保留已填写的有效内容:

const onChangeTickets = (e) => {
  const selectedCount = parseInt(e.target.value, 10);
  
  setFieldValue('numberOfTickets', selectedCount);
  
  const updatedTickets = Array.from({ length: selectedCount }, (_, idx) => {
    return values.tickets[idx] || '';
  });
  
  setFieldValue('tickets', updatedTickets);
};

4. 表单渲染示例

在Formik组件中动态渲染对应数量的输入框,并展示验证错误:

<Formik
  initialValues={initialValues}
  validationSchema={validationSchema}
  onSubmit={(values) => {
    alert(JSON.stringify(values, null, 2));
  }}
>
  {({ values, errors, touched, setFieldValue }) => (
    <Form>
      <div>
        <label>选择购票数量:</label>
        <Field
          name="numberOfTickets"
          as="select"
          onChange={onChangeTickets}
        >
          <option value="">请选择</option>
          {[...Array(10).keys()].map(num => (
            <option key={num + 1} value={num + 1}>
              {num + 1} 张
            </option>
          ))}
        </Field>
        {errors.numberOfTickets && touched.numberOfTickets && (
          <div className="error">{errors.numberOfTickets}</div>
        )}
      </div>

      {values.numberOfTickets > 0 && (
        <div className="ticket-fields">
          {values.tickets.map((_, idx) => (
            <div key={idx} className="ticket-item">
              <label>购票人 {idx + 1}</label>
              <Field
                name={`tickets[${idx}]`}
                type="text"
                placeholder="请填写购票人姓名/标识"
              />
              {errors.tickets && errors.tickets[idx] && touched.tickets?.[idx] && (
                <div className="error">{errors.tickets[idx]}</div>
              )}
            </div>
          ))}
        </div>
      )}

      {errors.tickets && typeof errors.tickets === 'string' && (
        <div className="global-error">{errors.tickets}</div>
      )}

      <button type="submit">购票</button>
      <button type="reset">重置</button>
    </Form>
  )}
</Formik>

补充说明

  • 重置按钮会触发Formik内置的resetForm方法,自动将表单恢复至initialValues状态,清空所有购票人输入并重置数量选择。
  • 唯一性验证仅针对非空元素生效,空输入只会触发必填错误,不会干扰重复判定。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 02:45:16