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

React Hook Form结合Yup动态Schema验证失效及场景值获取问题

React Hook Form + Yup 动态切换验证Schema的解决方案

核心问题拆解

你遇到的两个问题:scenario变更时Schema不更新、无法获取scenario字段值,本质是没用到React Hook Form提供的setResolver API,以及对表单字段监听方式不对。


1. 解决「无法获取scenario字段值」的问题

如果scenario是表单内的字段,直接用watch('scenario')就能实时拿到当前值,不需要等表单提交或依赖getValues。如果是外部状态(比如父组件props、useState),直接监听对应状态即可。


2. 动态更新验证Schema的正确姿势

reset方法确实不支持传入resolver,正确做法是用useForm返回的setResolver API,配合useEffect监听scenario的变化,实时切换验证规则。

示例代码:

import { useForm, yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';

// 定义基础验证规则
const baseSchema = yup.object().shape({
  username: yup.string().required('用户名不能为空'),
  scenario: yup.string().required('请选择场景'),
});

// 场景A专属规则
const scenarioASchema = baseSchema.concat(
  yup.object().shape({
    email: yup.string().email('邮箱格式错误').required('邮箱不能为空'),
  })
);

// 场景B专属规则
const scenarioBSchema = baseSchema.concat(
  yup.object().shape({
    phone: yup.string().matches(/^1[3-9]\d{9}$/, '手机号格式错误').required('手机号不能为空'),
  })
);

function DynamicForm() {
  const { register, watch, setResolver, reset, formState: { errors } } = useForm({
    // 初始加载用基础规则
    resolver: yupResolver(baseSchema),
  });

  // 实时监听scenario字段的当前值
  const currentScenario = watch('scenario');

  // 监听场景变化,切换验证规则
  useEffect(() => {
    let targetResolver;
    switch (currentScenario) {
      case 'A':
        targetResolver = yupResolver(scenarioASchema);
        break;
      case 'B':
        targetResolver = yupResolver(scenarioBSchema);
        break;
      default:
        targetResolver = yupResolver(baseSchema);
    }
    // 更新验证规则
    setResolver(targetResolver);

    // 可选:切换场景时清空非当前场景的字段值
    reset((prevValues) => ({
      ...prevValues,
      email: currentScenario === 'A' ? prevValues.email : '',
      phone: currentScenario === 'B' ? prevValues.phone : '',
    }));
  }, [currentScenario, setResolver, reset]);

  return (
    <form>
      <div>
        <label>用户名</label>
        <input {...register('username')} />
        {errors.username && <span className="error">{errors.username.message}</span>}
      </div>

      <div>
        <label>选择场景</label>
        <select {...register('scenario')}>
          <option value="">请选择</option>
          <option value="A">场景A(需填写邮箱)</option>
          <option value="B">场景B(需填写手机号)</option>
        </select>
        {errors.scenario && <span className="error">{errors.scenario.message}</span>}
      </div>

      {/* 根据当前场景渲染对应字段 */}
      {currentScenario === 'A' && (
        <div>
          <label>邮箱</label>
          <input {...register('email')} />
          {errors.email && <span className="error">{errors.email.message}</span>}
        </div>
      )}

      {currentScenario === 'B' && (
        <div>
          <label>手机号</label>
          <input {...register('phone')} />
          {errors.phone && <span className="error">{errors.phone.message}</span>}
        </div>
      )}
    </form>
  );
}

关键注意事项

  • 不要用reset传递resolver,reset的作用是重置表单值,更新验证规则必须用setResolver。
  • 如果scenario是外部状态(比如props),把useEffect的依赖换成props.scenario即可,无需用watch。
  • 切换Schema后如果需要保留当前场景的字段值,在reset里做针对性处理,避免全部清空。

内容的提问来源于stack exchange,提问作者SELVA MURUGAN

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 04:20:55