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

React Hook Form结合Zod处理数组邮箱输入的同步与初始值问题

React Hook Form + Zod 邮箱数组输入框问题解决办法

问题场景

页面接收emails: string[]类型的初始邮箱数据,通过emailsFields.map((field, index) => {...})渲染对应数量的输入框,同时用Zod配置了如下校验规则:

export const emails = validationService
  .string({
    required_error: '邮箱为必填项'
  })
  .email('请输入有效的邮箱地址')
  .array()

遇到两个矛盾的问题:

  • 把输入框的name设为emails时,初始邮箱值能正常显示,但在任意输入框输入内容时,所有输入框的值会同步变化
  • 改name为name={[${field.id}].emails}后,输入框不再同步变化,但初始接收的邮箱值无法渲染到输入框里

解决步骤及代码示例

核心问题原因

[${field.id}].emails的写法会把表单字段结构改成嵌套格式,和你传入的string[]初始数据、Zod数组校验规则不匹配,导致初始值无法绑定;而直接用emails作为name会让所有输入框绑定到同一个数组字段,所以会同步变化。

正确实现方式

  1. 配置useFieldArray的正确字段名
    用useFieldArray管理数组字段时,要指定对应数组的字段名(这里是emails),每个输入框的name要对应数组的索引位置,格式为emails.${index}或emails[${index}]。

  2. 正确传入初始值
    在调用useForm时,把接收的初始邮箱数组传入defaultValues的emails字段,确保useFieldArray能读取并初始化输入框。

  3. 保持Zod校验规则匹配
    你的Zod规则string().array()刚好对应emails数组字段,不需要修改。

完整代码示例

import { useForm, useFieldArray } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
// 替换成你的校验服务引入路径
import { validationService } from './path/to/validation-service';

// Zod校验规则
const emailSchema = validationService
  .string({
    required_error: '邮箱为必填项'
  })
  .email('请输入有效的邮箱地址')
  .array();

const EmailInputList = ({ emails: initialEmails }) => {
  const { register, control, formState: { errors } } = useForm({
    resolver: zodResolver(emailSchema),
    defaultValues: {
      // 传入初始邮箱数组,为空时设默认空数组
      emails: initialEmails || []
    }
  });

  const { fields, append, remove } = useFieldArray({
    control,
    // 对应表单中的数组字段名
    name: 'emails'
  });

  return (
    <div>
      {fields.map((field, index) => (
        <div key={field.id} style={{ marginBottom: '8px' }}>
          <input
            {...register(`emails.${index}`)}
            placeholder="请输入邮箱地址"
            type="email"
          />
          {/* 显示对应索引的错误提示 */}
          {errors.emails?.[index] && (
            <p style={{ color: 'red', margin: '4px 0 0 0' }}>
              {errors.emails[index].message}
            </p>
          )}
          <button
            type="button"
            onClick={() => remove(index)}
            style={{ marginLeft: '8px' }}
          >
            删除
          </button>
        </div>
      ))}
      <button type="button" onClick={() => append('')}>
        添加邮箱
      </button>
    </div>
  );
};

export default EmailInputList;

关键说明

  • 输入框name用emails.${index}是React Hook Form推荐的数组字段绑定方式,能正确关联到数组的对应索引项,不会出现同步变化的问题
  • 必须在defaultValues中设置emails字段的初始值,否则useFieldArray无法初始化渲染初始的邮箱内容
  • 如果需要动态添加/删除输入框,直接用useFieldArray提供的append和remove方法即可,内部会自动维护数组的状态和索引

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 16:46:15