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

如何使用yup与react-hook-form验证多账号登录表单的数组对象字段

基于Yup实现多组账号表单验证问题排查

我最近开始使用Yup做表单验证,重写登录页时遇到了问题:该页面会根据用户持有的账号数量展示多组登录名、密码字段。
原有的验证逻辑运行正常,但我们希望改用Yup实现对应能力。我编写了简单的Yup验证schema但无法正常运行,恳请大家协助排查问题,如有需要补充的信息可以告知我。

效果示意图

预期效果

  • 新增输入字段时,Yup能自动生成对应的验证规则
  • 单独验证数组内每个对象的字段:目前测试发现下面的示例数组会被整体判定验证失败,我只希望第一个对象的login_name单独验证失败
const invalid_example = [
{
login_name: ''
login_password: 'value' 
}, 
{
login_name: 'value'
login_password: 'value' 
}];

代码示例

state定义

const [validation, setValidation] = useState({
    users: [{ login_name: "notActive", login_password: "notActive" }]
  });
  const [login, setLogin] = useState({
    users: [{ login_name: "", login_password: "" }],
  });

输入字段渲染

{login.users.map((item, i) => (
            <Grid container direction="row" key={i} spacing={1}>
              <Grid item xs={5} sm={5} md={5} lg={5} xl={5}>
                <TextField
                  {...register(`users.${i}.login_name`, { shouldUnregister: true })}
                  id={i}
                  variant="outlined"
                  type="text"
                  name={`users${[i]}.login_name`}
                  value={login.users.login_name}
                  onChange={(e) => handleChange(e, i)}
                  label={props.t("login:Your Login Name")}
                  error={ 
                    Object.keys(errors).length > 0
                      ? errors.users[i].login_name.message !== undefined
                      : validation.users[i].login_name === false
                  }
                  helperText={
                    Object.keys(errors).length > 0
                      ? props.t(`register:${errors.users[i].login_name.message}`)
                      : validation.users[i].login_name === false &&
                       props.t("login:LoginName")
                  }
                  fullWidth
                  required
                />
              </Grid>
              <Grid item xs={5} sm={5} md={5} lg={5} xl={5}>
                <TextField
                  {...register(`users.${i}.login_password`, { shouldUnregister: true })}
                  id={i}
                  variant="outlined"
                  type="password"
                  name={`users${[i]}.login_password`}
                  value={login.users.login_password}
                  onChange={(e) => handleChange(e, i)}
                  label={props.t("login:Your Password")}
                  error={
                    Object.keys(errors).length > 0
                      ? errors.users[i].login_password.message !== undefined
                      : validation.users[i].login_password === false
                  }
                  helperText={
                    Object.keys(errors).length > 0
                      ? props.t(`register:${errors.users[i].login_password.message}`)
                      : validation.users[i].login_password === false &&
                      props.t("login:Password")
                  }
                  fullWidth
                  required
                />
              </Grid>
            </Grid>
          ))}

handleChange函数

const handleChange = (e, i) => {
  const { name, value} = e.target;
      const username = name.split('.', 3)[1]
      let users = [...login.users];
      users[i] = { ...users[i], [username]: value };
      setLogin({ users: users });
      if (username === "login_name" || username === "login_password") {
        let val = [...validation.users];
        let newName = username === "login_password" ? "login_name" : "login_password";
        let newValue = username === "login_password" ? val[i].login_name : val[i].login_password;
        if (value === "") {
          val[i] = { ...users[i], [username]: false, [newName]: newValue };
          setValidation({ users: val });
        } else {
          val[i] = { ...users[i], [username]: true, [newName]: newValue };
          setValidation({ users: val });
        }
      }
}

Yup验证schema

const LoginSchema = yup.object().shape({
  users: yup.array().ensure().of(
    yup.object().shape({
      login_name: yup.string().required(empty_field),
      login_password: yup.string().required(empty_field),
    })).required(empty_field)
});

当前存在的问题

  • 只要有一个字段为空,整个登录表单就会验证失败,需要实现每个字段单独校验
  • 当所有字段都填写了有效值时,表单无法验证通过
  • 触发表单验证后再新增登录字段会报错,Yup只会读取验证前已存在的对象规则,不会适配新增字段

修复方案

1. 统一字段命名

你在register里用的字段名是users.${i}.login_name,但手动给TextField绑定的name是users${[i]}.login_name,两者格式不一致导致Yup无法正确匹配验证结果,直接修改TextField的name属性即可:

// 登录名字段修改
name={`users.${i}.login_name`}
// 密码字段同理修改
name={`users.${i}.login_password`}

2. 移除冲突的自定义验证逻辑

你同时使用了React Hook Form的内置验证和自定义的validation状态控制错误显示,两者逻辑冲突会导致验证结果异常。建议直接使用React Hook Form返回的errors对象判断错误状态,删除自定义的validation相关state和handleChange里的validation修改逻辑:

// 错误状态判断简化为
error={!!errors?.users?.[i]?.login_name}
// 提示文案同理简化
helperText={errors?.users?.[i]?.login_name ? props.t(`register:${errors.users[i].login_name.message}`) : ''}

3. 适配新增字段验证

你已经配置了shouldUnregister: true,字段卸载时会自动注销验证规则,新增字段时只要保证新增的users数组项结构符合schema要求,Yup会自动适配新增项的验证规则,新增账号的逻辑可参考如下写法:

const addUser = () => {
  setLogin(prev => ({
    ...prev,
    users: [...prev.users, { login_name: "", login_password: "" }]
  }))
}

4. Schema优化

现有schema结构无需修改,只需提前定义好empty_field对应的错误提示字符串即可,修改后所有数组项会独立校验,单个字段错误不会影响其他正常填写的账号项。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 22:51:00