如何让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
相关产品推荐
相关产品推荐

