如何在nextjs 14.2中使用react-hook-from和zod发出POST请求

问题描述 投票:0回答:1

我是初学者。我正在尝试向快递服务器发出发布请求。我正在使用

typeScript
Shadcn/ui 
表单以及
zod
验证。我必须发出像下面这个 JSON 对象一样的 POST 请求。我从服务器动态获取位置。 如何发送请求?

{
  "description": "New Activity",
  "price": 50,
  "durationInMinutes": 120,
  "placeId":1,
  "locationId":1,
  "status": "available"
} 

这是我发出 POST 请求的表单代码,但这不起作用。验证如下

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(),
  price: z.number(),
  status: z
    .string({
      required_error: "Status required",
      invalid_type_error: "Description must be characters",
    })
    .min(1, "Status is required"),
  location: z.string({
    required_error: "Place required",
    invalid_type_error: "Please select the place",
  }),
});

每次我提交表单时,zod 验证都会抛出错误,如 预期的数字,但得到字符串,而我的验证是数字。


  const [loading, setLoading] = useState(false);
  const [locations, setLocations] = useState<Location[]>([]);

  const form = useForm<z.infer<typeof createActivityValidation>>({
    resolver: zodResolver(createActivityValidation),
  });

  const onSubmit = async (value: z.infer<typeof createActivityValidation>) => {
    console.log("value", value);
    try {
      setLoading(true);

      const activityConfig = {
        description: value.description,
        durationInMinutes: value.durationInMinutes,
        price: value.price,
        status: value.status,
        location: value.location,
      };

      const activityCreation = await fetcher.post(
        "activities/activities",
        activityConfig,
      );
      toast.success("Activity Created Successfully!");
      console.log(activityCreation.data);
    } catch (error) {
      setLoading(false);
      toast.error("Failed to create new Activity", {
        description: "Something went wrong Please try later",
      });
      console.log(error);
    }
  };

  return (
        <Form {...form}>
          <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-2">
            <FormField
              control={form.control}
              name="description"
              render={({ field }) => (
                <FormItem>
                  <FormLabel>Description</FormLabel>
                  <FormControl>
                    <Input
                      type="text"
                      placeholder="Activity Description"
                      {...field}
                    />
                  </FormControl>
                  <FormMessage />
                </FormItem>
              )}
            />
            <FormField
              control={form.control}
              name="durationInMinutes"
              render={({ field }) => (
                <FormItem>
                  <FormLabel>Duration</FormLabel>
                  <FormControl>
                    <Input
                      type="number"
                      placeholder="Duration of activities in minutes"
                      onChange={(e) => {
                        const value = Number(e.target.value);
                        field.onChange(value);
                      }}
                    />
                  </FormControl>
                  <FormMessage />
                </FormItem>
              )}
            />
            <FormField
              control={form.control}
              name="price"
              render={({ field }) => (
                <FormItem>
                  <FormLabel>Price</FormLabel>
                  <FormControl>
                    <Input
                      type="number"
                      placeholder="Price"
                      onChange={(e) => {
                        const value = Number(e.target.value);
                        field.onChange(value);
                      }}
                    />
                  </FormControl>
                  <FormMessage />
                </FormItem>
              )}
            />

            <FormField
              control={form.control}
              name="status"
              render={({ field }) => (
                <FormItem>
                  <FormLabel>Status</FormLabel>
                  <FormControl>
                    <Input
                      type="text"
                      placeholder="available or unavailabe"
                      {...field}
                    />
                  </FormControl>
                  <FormMessage />
                </FormItem>
              )}
            />
            <FormField
              control={form.control}
              name="status"
              render={({ field }) => (
                <FormItem>
                  <FormLabel>Status</FormLabel>
                  <FormControl>
                    <LocationSelector
                      value={field.value}
                      onChange={field.onChange}
                    />
                  </FormControl>
                  <FormMessage />
                </FormItem>
              )}
            />
             <Button
                variant="outline"
                size="sm"
                type="submit"
                className="bg-primary text-secondary rounded-full gap-2"
                disabled={loading}
              >
                {loading ? (
                  <span className="flex">
                    <CircleDashed size={20} className=" mr-1 animate-spin" />
                    Loading
                  </span>
                ) : (
                  <span className="flex">
                    <PlusIcon size={20} />
                    Create
                  </span>
                )}
          </Button>
  );
}

typescript react-hook-form zod nextjs14 shadcnui
1个回答
0
投票

这里的问题是,即使您在持续时间和价格的输入字段上设置 type="number",提交收到的数据时的表单中也是字符串。

您可以通过两种方式完成。

  1. 使用 z.number.or(z.string()) 代替 z.number() 并在 onSubmit 内部手动类型转换为

    数据.价格 = 数字(数据.价格)

  2. 使用react-hook-form中的控制器在每个onChange事件上将输入字符串类型转换为数字。

从“react-hook-form”导入 { useForm, Controller, SubmitHandler };

const {
    handleSubmit,
    control,
    register,
    formState: { errors },
  } = useForm<SchemaType>({
    resolver: zodResolver(schema),
  });

<Controller
              control={control}
              name="price"
              render={({
                field: { value, onChange },
              }) => (
                <input
                  value={value}
                  placeholder="Enter price"
                  type="number"
                  onChange={(e) => onChange(Number(e.target.value))}
                />
              )}
            />

© www.soinside.com 2019 - 2024. All rights reserved.