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

Next.js14+Zod+shadcn/ui+React Hook Form多复选框实现问题

解决多选复选框字符串数组的Zod与React Hook Form配置问题

核心问题分析

你的Zod Schema reservationMethod: z.string().array() 本身是正确的,但表单实现有两处关键错误:

  • handleCheckboxChange 未正确通过React Hook Form的field.onChange更新字段值
  • 错误地将reservationMethod字段的props绑定到了"Name of agency"输入框,导致字段类型冲突(数组 vs 字符串)

修正后的完整实现

1. 调整Zod Schema(按需添加校验)

如果需要用户至少选一个选项,可添加.nonempty()校验;若可选则保留原写法:

import { z } from "zod";

const formSchema = z.object({
  reservationMethod: z.string().array().nonempty("请选择至少一种预订方式"), // 可选:非空校验
  agencyName: z.string().optional(), // 新增代理名字段,对应独立输入框
});

2. 修正表单组件代码

移除自定义的handleCheckboxChange,直接通过field.onChange更新数组,同时将输入框拆分为独立字段:

import { FormField, FormItem, FormLabel, FormControl } from "@/components/ui/form";
import { Checkbox } from "@/components/ui/checkbox";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";

// 在Form组件内部:
<FormField
  name="reservationMethod"
  control={form.control}
  render={({ field }) => (
    <FormItem className="flex flex-col gap-4">
      <FormLabel className="text-base md:text-lg text-black font-extralight">
        Which of the following did you use to make your reservation?
      </FormLabel>
      <FormControl>
        <div className="flex gap-4 flex-wrap">
          {/* 用数组统一管理选项,减少重复代码 */}
          {[
            { id: "tourOperator", label: "Tour Operator", value: "Tour Operator" },
            { id: "internet", label: "Internet", value: "Internet" },
            { id: "directlyWithResort", label: "Directly with the resort", value: "Directly with the resort" },
            { id: "travelAgent", label: "Travel Agent", value: "Travel Agent" },
          ].map((option) => (
            <div key={option.id} className="flex items-center gap-1">
              <Checkbox
                id={option.id}
                checked={field.value.includes(option.value)}
                onCheckedChange={(checked) => {
                  // 选中则添加值,取消则移除值
                  if (checked) {
                    field.onChange([...field.value, option.value]);
                  } else {
                    field.onChange(field.value.filter(val => val !== option.value));
                  }
                }}
              />
              <Label htmlFor={option.id}>{option.label}</Label>
            </div>
          ))}
        </div>
      </FormControl>
    </FormItem>
  )}
/>

{/* 代理名字段拆分为独立FormField */}
<FormField
  name="agencyName"
  control={form.control}
  render={({ field }) => (
    <FormItem className="flex items-center gap-2 mt-4">
      <FormLabel className="text-base text-black font-extralight">Name of agency:</FormLabel>
      <FormControl>
        <Input type="text" {...field} placeholder="请输入代理名称" />
      </FormControl>
    </FormItem>
  )}
/>

3. 确保表单初始值正确

创建表单时,给reservationMethod设置空数组初始值,匹配Zod的array类型:

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

const form = useForm({
  resolver: zodResolver(formSchema),
  defaultValues: {
    reservationMethod: [],
    agencyName: "",
  },
});

关键说明

  • 完全通过React Hook Form的field.value和field.onChange管理复选框状态,确保表单状态一致
  • 每个输入项对应独立表单字段,避免类型冲突
  • 用数组映射渲染复选框,简化代码维护

内容的提问来源于stack exchange,提问作者Aidan Mc Intosh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 01:43:12