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

如何让Zod为单个输入返回多条验证错误?

解决Zod结合React Hook Form返回多条密码错误的问题

核心问题原因

  • Zod默认的链式验证(如.min().regex())是短路验证:只要第一个验证失败,后续验证会直接停止,仅返回第一条错误。
  • 若未正确获取React Hook Form中的错误集合,即便Zod返回多条错误,也无法展示全部内容。

方法一:用superRefine手动添加所有错误

你编写的superRefine代码逻辑本身正确,可添加多条错误,只需确保在React Hook Form中正确获取并渲染所有错误。以下是完整实现示例:

1. 定义Zod Schema

import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

const PasswordSchema = z.object({
  password: z.string().superRefine((val, ctx) => {
    // 验证密码长度
    if (val.length < 8) {
      ctx.addIssue({
        code: z.ZodIssueCode.too_small,
        minimum: 8,
        type: "string",
        message: "密码长度不能少于8位",
      });
    }
    // 验证是否包含大写字母
    if (!/[A-Z]/.test(val)) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        message: "密码必须包含至少一个大写字母",
      });
    }
    // 验证是否包含数字
    if (!/\d/.test(val)) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        message: "密码必须包含至少一个数字",
      });
    }
  }),
});

type FormValues = z.infer<typeof PasswordSchema>;

2. 在React组件中使用并渲染所有错误

export default function PasswordForm() {
  const {
    register,
    formState: { errors },
  } = useForm<FormValues>({
    resolver: zodResolver(PasswordSchema),
    defaultValues: { password: "" },
    mode: "onChange", // 实时触发验证
  });

  // 获取密码字段的所有错误数组
  const passwordErrors = errors.password?.root?.issues || [];

  return (
    <div>
      <input
        type="password"
        {...register("password")}
        placeholder="输入密码"
      />
      {/* 循环渲染所有错误 */}
      {passwordErrors.length > 0 && (
        <ul style={{ color: "red", listStyle: "none", padding: 0 }}>
          {passwordErrors.map((issue, index) => (
            <li key={index}>{issue.message}</li>
          ))}
        </ul>
      )}
    </div>
  );
}

方法二:替代方案(避免链式短路验证)

若偏好链式语法,可将多个验证逻辑整合到单个refine中,避免Zod的短路行为:

password: z.string().refine((val) => {
  const errors = [];
  if (val.length < 8) errors.push("密码长度不能少于8位");
  if (!/[A-Z]/.test(val)) errors.push("密码必须包含至少一个大写字母");
  if (!/\d/.test(val)) errors.push("密码必须包含至少一个数字");
  return errors.length === 0;
}, {
  message: (val) => {
    const errors = [];
    if (val.length < 8) errors.push("密码长度不能少于8位");
    if (!/[A-Z]/.test(val)) errors.push("密码必须包含至少一个大写字母");
    if (!/\d/.test(val)) errors.push("密码必须包含至少一个数字");
    return errors.join("; ");
  },
})

这种方式会将所有错误拼接成一条消息,若需单独渲染每条错误,仍推荐使用superRefine方案。


注意事项

  • 确保@hookform/resolvers依赖为最新版本,旧版本可能存在错误收集不全的问题。
  • 不要在superRefine中提前return,否则后续的addIssue不会执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 19:12:14