Next.js14.2中react-hook-form+Zod发送POST请求的问题排查
问题解决:Zod验证"Expected number but got string"错误及正确发送POST请求
问题核心分析
你遇到的错误来自三个关键问题:
- Zod验证Schema与服务器要求的JSON结构不匹配,字段类型定义错误
- 表单字段绑定重复,导致位置选择器关联了错误的字段
- 数字输入框未完整同步表单状态
分步解决方案
1. 修正Zod验证Schema
服务器需要locationId(number类型),但你的验证里定义的是location(string类型),同时补充服务器要求的字段结构:
import { z } from "zod"; export const createActivityValidation = z.object({ description: z .string({ required_error: "Description required", invalid_type_error: "Description must be characters", }) .min(1, "Description Needs More than 1 character"), durationInMinutes: z.number({ required_error: "Duration required", invalid_type_error: "Duration must be a number", }), price: z.number({ required_error: "Price required", invalid_type_error: "Price must be a number", }), status: z .string({ required_error: "Status required", invalid_type_error: "Status must be characters", }) .min(1, "Status is required") .refine(val => ["available", "unavailable"].includes(val), { message: "Status must be 'available' or 'unavailable'" }), locationId: z.number({ required_error: "Location required", invalid_type_error: "Please select a valid location", }), // 若服务器需要placeId,补充以下字段(根据实际需求调整) // placeId: z.number({ required_error: "Place required" }), });
2. 修正表单字段绑定
你有两个name="status"的FormField,将第二个改为name="locationId",确保位置选择器关联正确字段:
// 替换原来重复的status FormField <FormField control={form.control} name="locationId" render={({ field }) => ( <FormItem> <FormLabel>Location</FormLabel> <FormControl> {/* 确保LocationSelector的value转换为number类型 */} <LocationSelector value={field.value} onChange={(val) => field.onChange(Number(val))} /> </FormControl> <FormMessage /> </FormItem> )} />
3. 完善数字输入框的状态同步
原来的数字输入框仅处理了onChange,未传递value导致状态不同步,补充绑定:
// durationInMinutes输入框示例(price字段同理修改) <FormField control={form.control} name="durationInMinutes" render={({ field }) => ( <FormItem> <FormLabel>Duration</FormLabel> <FormControl> <Input type="number" placeholder="Duration of activities in minutes" value={field.value ?? ""} onChange={(e) => { const value = e.target.value ? Number(e.target.value) : undefined; field.onChange(value); }} /> </FormControl> <FormMessage /> </FormItem> )} />
4. 调整请求数据结构
确保提交的JSON与服务器要求完全一致:
const onSubmit = async (value: z.infer<typeof createActivityValidation>) => { console.log("value", value); try { setLoading(true); // 严格匹配服务器需要的JSON结构 const activityConfig = { description: value.description, price: value.price, durationInMinutes: value.durationInMinutes, placeId: 1, // 若为动态值,需从表单/状态中获取对应number类型数据 locationId: value.locationId, status: value.status, }; const activityCreation = await fetcher.post( "activities/activities", activityConfig, ); toast.success("Activity Created Successfully!"); console.log(activityCreation.data); setLoading(false); } catch (error) { setLoading(false); toast.error("Failed to create new Activity", { description: "Something went wrong Please try later", }); console.log(error); } };
额外注意事项
- 确保
LocationSelector组件返回的是可转换为数字的有效值,或在onChange中强制转换为Number - 若
placeId是动态获取的,需在表单中添加对应的字段和验证规则 - 数字输入框处理空值时,建议设置为
undefined而非空字符串,避免Zod验证失败
内容的提问来源于stack exchange,提问作者Hrittik Bhattacharjee
相关产品推荐
相关产品推荐

