React Hook Form + Zod 表单页面加载时触发Zod校验错误的修复求助
你好呀~ 这个问题我之前也碰到过!页面刚加载就爆Zod校验错误,核心原因是你设置的表单默认值完全不符合Zod Schema的校验规则,而且虽然你指定了mode: "onSubmit",但zod-resolver在表单初始化阶段就会对默认值执行一次校验,直接抛出了不符合规则的错误。
下面给你几个可行的修复方案,你可以根据自己的业务需求选择:
方案一:调整默认值,匹配Zod校验规则(快速临时修复)
如果只是想先消掉错误,你可以把默认值改成刚好满足Schema最低要求的内容:
const form = useForm<formFields>({ defaultValues: { name: ' ', // 两个空格,满足min(2)要求 description: 'a'.repeat(50), // 50个重复字符,满足min(50) ingredients: ['默认食材'], // 非空字符串,匹配数组元素的min(1) foodPrice: 1, // 大于等于1,满足min(1) foodQuantity: 1, availableOn: new Date().toISOString() // 生成合法的ISO datetime字符串 }, resolver, mode: "onSubmit", });
👉 缺点:初始值不够友好,用户打开页面会看到占位内容,不是空白表单。
方案二:修改Zod Schema,允许初始空值(推荐,符合用户体验)
更合理的做法是让Schema兼容空白的初始值,只在提交时强制校验必填规则。你可以用Zod的optional()结合refine()来实现:
import { z as zod } from 'zod'; // 调整后的Schema const schema = zod.object({ // 允许初始为空,提交时必须满足长度要求且非空 name: zod.string() .min(2, "Name must be at least 2 characters.") .optional() .refine(val => val?.trim() !== '', "Name is required"), description: zod.string() .min(50, "Description must be at least 50 characters.") .optional() .refine(val => val?.trim() !== '', "Description is required"), // 数组允许初始有空字符串,提交时确保至少1个非空元素 ingredients: zod.array( zod.string().min(1, "Ingredient cannot be empty").optional() ).min(1, "At least one ingredient is required") .refine(arr => arr.every(item => item?.trim() !== ''), "All ingredients cannot be empty"), // 价格/数量允许初始为0,提交时必须大于等于1 foodPrice: zod.number() .min(1, "Price must be at least 1") .optional() .refine(val => val !== undefined && val > 0, "Price is required"), foodQuantity: zod.number() .min(1, "Quantity must be at least 1") .optional() .refine(val => val !== undefined && val > 0, "Quantity is required"), // 日期允许初始为空,提交时必须是合法的ISO datetime availableOn: zod.string() .datetime("Please enter a valid ISO datetime") .optional() .refine(val => val !== undefined && val !== '', "Availability date is required"), });
然后保持你的默认值为空白状态即可:
const form = useForm<formFields>({ defaultValues: { name: '', description: '', ingredients: [''], foodPrice: undefined, // 改成undefined,对应Schema里的optional() foodQuantity: undefined, availableOn: '' }, resolver: zodResolver(schema), mode: "onSubmit", });
👉 优点:用户打开页面看到的是空白表单,体验更自然,只有在提交时才会触发完整校验,提示错误。
方案三:禁止React Hook Form挂载时校验
如果不想修改Schema和默认值,你可以明确告诉useForm不要在组件挂载时执行校验,添加shouldValidateOnMount: false配置:
const form = useForm<formFields>({ defaultValues: { name: '', description: '', ingredients: [''], foodPrice: 0, foodQuantity: 0, availableOn: '' }, resolver, mode: "onSubmit", shouldValidateOnMount: false, // 关键:禁用挂载时的校验 });
👉 注意:这个方案只是隐藏了初始化时的错误,但用户提交表单时还是会因为默认值不符合规则而报错,所以你需要确保用户在提交前填写了所有正确的内容,适合临时调试用。
额外提醒:关于Ingredients字段
你的Ingredients是数组类型,默认值是[''],Zod的array(zod.string().min(1)).min(1)会同时校验数组长度≥1和数组内每个元素长度≥1,所以初始的空字符串会触发错误。如果用方案二的Schema,已经处理了这个问题;如果用方案三,你可能需要在用户输入时动态添加/删除数组元素,确保提交时数组内的元素都不为空。
如果还有问题,可以告诉我你的业务场景,我再帮你调整细节~




