Zod superRefine动态更新失效:react-hook-form条件验证问题
问题解决思路与方案
一、条件验证错误实时更新问题
你的superRefine规则依赖contactType字段,但React Hook Form在提交后切换到onChange验证模式时,只会监听字段自身的变更,不会自动触发依赖字段变化后的验证。要实现切换contactType时实时更新错误信息,只需监听contactType的变化,手动触发对应字段的验证:
import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import z from "zod"; const schema = z .object({ email: z.string().email().optional(), userName: z.string().optional(), contactType: z.string(), }) .superRefine((data, ctx) => { if (data.contactType === "email" && !data.email) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["email"], message: "email is required", }); } else if (data.contactType === "userName" && !data.userName) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["userName"], message: "userName is required", }); } }); function FormComponent() { const { register, watch, trigger, formState: { errors } } = useForm({ resolver: zodResolver(schema), mode: "onSubmit" // 保留默认的提交时验证行为 }); const contactType = watch("contactType"); // 监听contactType变化,触发对应字段的验证 useEffect(() => { if (contactType === "email") { trigger("email"); } else if (contactType === "userName") { trigger("userName"); } }, [contactType, trigger]); // 表单渲染逻辑... }
这样提交表单后切换contactType,就能实时触发对应字段的验证,错误信息会同步更新。
二、动态register导致字段值串位问题
你推测的执行时机问题是核心,这里提供两个无需条件渲染多个输入框的优化方案:
方案1:用Controller组件精准控制字段绑定
Controller可以更灵活地控制字段的取值与更新逻辑,避免register自动绑定带来的串位问题:
import { useForm, Controller } from "react-hook-form"; function FormComponent() { const { control, watch, setValue, unregister } = useForm({ resolver: zodResolver(schema), mode: "onSubmit" }); const contactType = watch("contactType"); // 切换contactType时,清理旧字段并重置新字段值 useEffect(() => { const oldField = contactType === "email" ? "userName" : "email"; unregister(oldField); setValue(contactType, "", { shouldValidate: false }); }, [contactType, unregister, setValue]); return ( <Controller control={control} name={contactType} render={({ field }) => ( <input {...field} placeholder={contactType === "email" ? "输入邮箱" : "输入用户名"} /> )} /> ); }
方案2:给输入框添加唯一key触发组件重建
利用React的key机制,当contactType变化时强制重建输入框,避免复用旧DOM元素导致的值串位:
function FormComponent() { const { register, watch, unregister } = useForm({ resolver: zodResolver(schema), mode: "onSubmit" }); const contactType = watch("contactType"); useEffect(() => { const oldField = contactType === "email" ? "userName" : "email"; unregister(oldField); }, [contactType, unregister]); return ( <input key={contactType} // 用contactType作为key,切换时重建组件 {...register(contactType)} placeholder={contactType === "email" ? "输入邮箱" : "输入用户名"} /> ); }
内容的提问来源于stack exchange,提问作者Ido Kadosh
相关产品推荐
相关产品推荐

