添加Zod refine方法后表单onSubmit无法触发问题排查
问题描述
我编写了如下Zod表单校验schema:
const createTaskFormSchema = z .object({ duration: z .custom<number>() .refine((value) => value ?? false, "Required") .refine((value) => Number.isFinite(Number(value)), "Invalid number") .transform((value) => Number(value)), checkpoints: z .custom<number>() .refine((value) => value ?? false, "Required") .refine((value) => Number.isFinite(Number(value)), "Invalid number") .transform((value) => Number(value)), }) .refine((data) => data.duration < data.checkpoints, { message: "Checkpoints cannot be more than double the duration time", path: ["confirm"], });
搭配React Hook Form使用:
const form = useForm<z.infer<typeof createTaskFormSchema>>({ resolver: zodResolver(createTaskFormSchema), defaultValues: { duration: 0, checkpoints: 0, }, });
测试代码如下:
function onSubmit(values: z.infer<typeof createTaskFormSchema>) { console.log('in here'); try { const result = createTaskFormSchema.parse({ duration: 6, checkpoints: 3, }); } catch (err) { if (err instanceof z.ZodError) { console.log(err.issues); } } }
但onSubmit函数完全没有触发(控制台未打印'in here'),移除对象级refine方法后就能正常进入onSubmit,请问哪里出错了?
问题原因
- 校验逻辑与提示信息矛盾:你设置的refine条件是
data.duration < data.checkpoints(即任务时长必须小于检查点数量才通过校验),但提示信息却是“Checkpoints cannot be more than double the duration time”(检查点数量不能超过任务时长的两倍),逻辑完全相反。比如测试用例里duration=6、checkpoints=3,6不小于3,直接触发校验失败。 - 错误路径指向不存在的字段:refine配置里的
path: ["confirm"],但你的表单schema中根本没有confirm这个字段。React Hook Form会因为这个不存在的字段校验失败,直接拦截表单提交,导致onSubmit无法执行。
修复方案
步骤1:修正校验逻辑
根据提示信息,正确的校验条件应该是检查点数量不超过任务时长的两倍,调整refine条件:
.refine((data) => data.checkpoints <= data.duration * 2, { message: "Checkpoints cannot be more than double the duration time", // 步骤2:指定正确的错误关联字段 path: ["checkpoints"], })
步骤2:修正错误路径
把path设置为表单中存在的字段(比如checkpoints),这样错误信息会关联到对应的输入框,同时避免因不存在的字段拦截提交。
完整修复后的schema
const createTaskFormSchema = z .object({ duration: z .custom<number>() .refine((value) => value ?? false, "Required") .refine((value) => Number.isFinite(Number(value)), "Invalid number") .transform((value) => Number(value)), checkpoints: z .custom<number>() .refine((value) => value ?? false, "Required") .refine((value) => Number.isFinite(Number(value)), "Invalid number") .transform((value) => Number(value)), }) .refine((data) => data.checkpoints <= data.duration * 2, { message: "Checkpoints cannot be more than double the duration time", path: ["checkpoints"], });
内容的提问来源于stack exchange,提问作者Mathew
相关产品推荐
相关产品推荐

