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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 18:45:27