You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

React Hook Form + Zod 表单页面加载时触发Zod校验错误的修复求助

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,已经处理了这个问题;如果用方案三,你可能需要在用户输入时动态添加/删除数组元素,确保提交时数组内的元素都不为空。

如果还有问题,可以告诉我你的业务场景,我再帮你调整细节~

火山引擎 最新活动