使用shadcn Select+react-hook-form时Zod接收undefined值问题求助
在React项目中使用shadcn Select组件、react-hook-form表单库结合Zod做校验时,点击提交按钮出现以下问题:
- 配置
onSubmit={form.handleSubmit(onSubmitSearch)}时,Zod校验不通过,onSubmitSearch无法执行 - 改为
onSubmit={onSubmitSearch}后,onSubmitSearch能执行,但formSchema.parse({ ...e })报错,所有字段值均为undefined
错误信息:
Unhandled Runtime Error ZodError: [
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": ["property_type"],
"message": "Required"
},
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": ["developer_id"],
"message": "Required"
},
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": ["location"],
"message": "Required"
}
]
- 字段名称不匹配:Zod Schema定义的字段是
developer_id和location,但FormField的name属性写的是Developer和Location(名称、大小写都不对应),导致react-hook-form无法正确收集这两个字段的值,校验自然失败。 - 错误获取表单值:直接解构表单事件
e拿不到react-hook-form管理的表单数据,e是DOM提交事件对象,不是表单值集合,所以formSchema.parse({ ...e })必然拿不到有效数据。 - Select组件绑定错误:shadcn的Select组件需要用
value属性绑定field.value来同步表单状态,defaultValue仅在初始渲染生效,后续值更新不会同步到表单状态中。
1. 修正FormField的name属性
将Developer改为developer_id,Location改为location,与Zod Schema保持完全一致:
// 修正developer_id字段的FormField <FormField control={form.control} name="developer_id" render={({ field }) => ( // 其余代码不变 )} /> // 修正location字段的FormField <FormField control={form.control} name="location" render={({ field }) => ( // 其余代码不变 )} />
2. 正确绑定Select组件的value属性
把Select的defaultValue替换为value,确保表单状态与组件值同步:
<Select onValueChange={field.onChange} value={field.value} // 替换defaultValue为value > // 其余代码不变 </Select>
3. 恢复react-hook-form的标准提交逻辑
重新使用form.handleSubmit(onSubmitSearch),并直接使用react-hook-form传递的校验后数据(无需手动调用Zod parse,resolver已完成校验):
// 修正提交函数,直接接收校验后的data const onSubmitSearch = (data) => { // 这里直接使用校验通过的表单数据 console.log("提交数据:", data); // 编写你的业务提交逻辑 }; // 表单onSubmit改回标准写法 <form onSubmit={form.handleSubmit(onSubmitSearch)} className="mt-10 mb-8 flex flex-col md:flex-row justify-center md:space-x-3 w-fit max-w-fit mx-auto" > // 其余代码不变 </form>
完整修正后的核心代码片段
const formSchema = z.object({ property_type: z.string(), developer_id: z.string(), location: z.string() }); const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { property_type: "", developer_id: "", location: "" } }); const onSubmitSearch = (data) => { console.log("校验通过的提交数据:", data); }; return ( <Form {...form}> <form onSubmit={form.handleSubmit(onSubmitSearch)} className="mt-10 mb-8 flex flex-col md:flex-row justify-center md:space-x-3 w-fit max-w-fit mx-auto" > <div className="flex flex-col md:flex-row space-y-3 md:space-y-0 md:space-x-3 justify-evenly w-fit"> <FormField control={form.control} name="property_type" render={({ field }) => ( <FormItem> <Select onValueChange={field.onChange} value={field.value} > <FormControl> <SelectTrigger className="py-1 px-2 rounded-md min-w-24 w-56 md:w-40 text-xs font-medium h-7 self-center"> <SelectValue placeholder="Property" /> </SelectTrigger> </FormControl> <SelectContent className="w-56 h-fit max-h-60 text-xs"> {/* 原有下拉内容不变 */} </SelectContent> </Select> <FormMessage /> </FormItem> )} /> <FormField control={form.control} name="developer_id" render={({ field }) => ( <FormItem> <Select onValueChange={field.onChange} value={field.value} > <FormControl> <SelectTrigger className="py-1 px-2 rounded-md min-w-24 w-56 md:w-40 text-xs font-medium h-7 self-center"> <SelectValue placeholder="Developer" /> </SelectTrigger> </FormControl> <SelectContent className="w-56 h-fit max-h-60 text-xs"> {/* 原有下拉内容不变 */} </SelectContent> </Select> <FormMessage /> </FormItem> )} /> <FormField control={form.control} name="location" render={({ field }) => ( <FormItem> <Select onValueChange={field.onChange} value={field.value} > <FormControl> <SelectTrigger className="py-1 px-2 rounded-md min-w-24 w-56 md:w-40 text-xs font-medium h-7 self-center"> <SelectValue placeholder="Location" /> </SelectTrigger> </FormControl> <SelectContent className="w-56 h-fit max-h-60 text-xs"> {/* 原有下拉内容不变 */} </SelectContent> </Select> <FormMessage /> </FormItem> )} /> </div> {/* 提交按钮部分不变 */} </form> </Form> );
内容的提问来源于stack exchange,提问作者alex1923

