You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js14.2中react-hook-form+Zod发送POST请求的问题排查

问题解决:Zod验证"Expected number but got string"错误及正确发送POST请求

问题核心分析

你遇到的错误来自三个关键问题:

  1. Zod验证Schema与服务器要求的JSON结构不匹配,字段类型定义错误
  2. 表单字段绑定重复,导致位置选择器关联了错误的字段
  3. 数字输入框未完整同步表单状态

分步解决方案

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 10:17:05