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

React Hook Form与Zod中select标签验证错误不消失问题

解决React Hook Form中Select选择后验证错误不消失的问题

问题场景

提交表单时,select标签因未选择选项会显示验证错误,但选中选项后,错误提示无法像firstName输入框那样自动消失。尝试用React Hook Form的trigger手动触发重验证也无效,相关代码如下:

表单Schema

const FormSchema = z.object({
  firstName: z
    .string()
    .min(1, { message: "Please provide a value" })
    .max(260, { message: "The name is too long" }),
  selectedDay: z
    .string()
    .nonempty({ message: "Please provide a value for day" })
});

RHF钩子初始化

const {
  register,
  formState: { errors },
  watch,
  handleSubmit,
  setValue,
  formState,
} = useForm<PersonalInformationFormInput>({
  resolver: zodResolver(FormSchema),
  defaultValues,
});

本地状态定义

const [selectedDay, setSelectedDay] = useState("");
const [days, setDays] = useState<SelectedValue[]>([]);
const [defaultValues, setDefaultValues] =
  useState<PersonalInformationFormInput>({
    firstName: "",
    selectedDay: "",
  });

自定义Change处理函数

const handleSelectedDayChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
  setValue("selectedDay", e.target.value);
  setSelectedDay(e.target.value);
};

Select组件代码

<div>
  <select
    className="w-full h-10 text-sm"
    {...register("selectedDay")}
    onChange={(e) => {
      handleSelectedDayChange(e);
      // trigger("selectedDay");
    }}
    value={selectedDay}
  >
    <option value="" disabled={true}>
      Day
    </option>
    {days.map((dayOption) => (
      <option key={dayOption.value} value={dayOption.value}>
        {dayOption.label}
      </option>
    ))}
  </select>
  {errors?.selectedDay?.message && (
    <p className="pl-4 lg:pl-8 min-[400px]:pl-6 text-red-700 mt-1">
      {errors.selectedDay.message}
    </p>
  )}
</div>

问题原因

核心问题是React Hook Form的内部状态与组件本地维护的selectedDay状态不一致:

  1. 同时用RHF的register和自定义useState控制select的value,导致值更新时,RHF状态和本地状态存在同步延迟;
  2. 自定义的handleSelectedDayChange中重复调用setValue和setSelectedDay,干扰了RHF的自动验证流程,使得选中选项后,验证逻辑未及时感知值的变化。

解决方案

移除本地维护的selectedDay状态,让React Hook Form完全接管select的值管理和验证触发:

步骤1:删除本地状态

移除自定义的selectedDay useState:

// 删掉这一行
// const [selectedDay, setSelectedDay] = useState("");

步骤2:用RHF的watch获取选中值

通过RHF提供的watch方法实时获取selectedDay的当前值:

const {
  register,
  formState: { errors },
  watch,
  handleSubmit,
  setValue,
  formState,
} = useForm<PersonalInformationFormInput>({
  resolver: zodResolver(FormSchema),
  defaultValues,
});

// 用watch获取selectedDay的实时值
const selectedDay = watch("selectedDay");

步骤3:简化Select组件

去掉自定义的onChange和手动值设置逻辑,让register完全控制select:

<div>
  <select
    className="w-full h-10 text-sm"
    {...register("selectedDay")}
    value={selectedDay}
  >
    <option value="" disabled={true}>
      Day
    </option>
    {days.map((dayOption) => (
      <option key={dayOption.value} value={dayOption.value}>
        {dayOption.label}
      </option>
    ))}
  </select>
  {errors?.selectedDay?.message && (
    <p className="pl-4 lg:pl-8 min-[400px]:pl-6 text-red-700 mt-1">
      {errors.selectedDay.message}
    </p>
  )}
</div>

步骤4:删除冗余的Change处理函数

直接删掉handleSelectedDayChange,因为register已经自动处理了值的更新和验证触发。

效果说明

修改后,选中select选项时,RHF会自动感知值的变化并触发验证,错误提示会像firstName输入框一样立即消失,无需手动调用trigger。

内容的提问来源于stack exchange,提问作者Dave Odipo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 01:54:58