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状态不一致:
- 同时用RHF的
register和自定义useState控制select的value,导致值更新时,RHF状态和本地状态存在同步延迟; - 自定义的
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
相关产品推荐
相关产品推荐

