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

如何在React Hook Form中借助Yup.lazy实现字段值变更时动态更新Yup校验Schema?

如何在React Hook Form中借助Yup.lazy实现字段值变更时动态更新Yup校验Schema?

嘿,我来帮你搞定这个动态校验的问题!你用React Hook Form结合Yup.lazy的思路完全找对方向了,刚好我之前也处理过类似场景,给你一步步拆解怎么落地实现。

首先得明确:Yup.lazy的核心作用就是让你根据当前表单的实时值,动态返回不同的校验规则,完美适配这种依赖其他字段(比如你这里的type)的动态校验需求。

第一步:补全表单初始化的核心代码

你之前的代码里resolv应该是没写完,正确的做法是用yupResolver把生成的lazy schema传给React Hook Form。先给你补全完整的初始化逻辑:

首先导入必要的依赖:

import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
import { useState } from 'react';

然后写生成动态Schema的getSchema函数,这里就是Yup.lazy发挥作用的地方:

const getSchema = () => {
  return yup.lazy((values) => {
    // 这里的values就是当前表单的所有值,我们根据type字段来分支校验规则
    switch (values.type) {
      // 当用户选择邮箱类型时,校验邮箱格式
      case 'email':
        return yup.object().shape({
          type: yup.string().required('类型不能为空'),
          contact: yup.string().email('请输入有效的邮箱地址').required('邮箱不能为空'),
          // 可以在这里添加其他和邮箱相关的字段校验,比如邮箱后缀限制等
        });
      // 当用户选择手机号类型时,校验手机号格式
      case 'phone':
        return yup.object().shape({
          type: yup.string().required('类型不能为空'),
          contact: yup.string().matches(/^1[3-9]\d{9}$/, '请输入有效的手机号').required('手机号不能为空'),
          // 同理,添加手机号相关的额外校验规则
        });
      // 默认情况,至少保证type字段必填
      default:
        return yup.object().shape({
          type: yup.string().required('类型不能为空'),
        });
    }
  });
};

接下来是表单的初始化和状态管理:

// 假设你用状态管理用户选择的type类型
const [selectedType, setSelectedType] = useState('email');
// 你的默认值获取逻辑保持不变
const defaultValues = getDefaultValues(selectedType, dataToEdit);
// 生成动态schema
const schema = getSchema();

// 初始化React Hook Form,关键是用yupResolver关联schema
const methods = useForm<any>({
  reValidateMode: 'onChange',
  resolver: yupResolver(schema), // 这里就是你之前没写完的部分
  defaultValues,
});

// 解构需要用到的表单方法
const { register, handleSubmit, reset, formState: { errors } } = methods;

第二步:编写表单UI并处理类型切换

在渲染表单的时候,要注意当type字段变更时,不仅要更新状态,还要调用reset方法重置表单默认值,这样新的schema才能正确关联到对应的字段,同时触发重新校验:

<form onSubmit={handleSubmit((formData) => {
  // 处理表单提交逻辑
  console.log('提交的数据:', formData);
})}>
  {/* 类型选择下拉框 */}
  <div className="form-group">
    <label>联系方式类型:</label>
    <select
      {...register('type')}
      value={selectedType}
      onChange={(e) => {
        const newType = e.target.value;
        setSelectedType(newType);
        // 重置表单,传入对应类型的默认值,触发重新校验
        reset(getDefaultValues(newType, dataToEdit));
      }}
    >
      <option value="email">邮箱</option>
      <option value="phone">手机号</option>
    </select>
    {/* 显示类型字段的错误提示 */}
    {errors.type && <p className="error-text">{errors.type.message}</p>}
  </div>

  {/* 联系方式输入框 */}
  <div className="form-group">
    <label>联系方式:</label>
    <input
      {...register('contact')}
      type="text"
      placeholder={selectedType === 'email' ? '请输入邮箱' : '请输入手机号'}
    />
    {/* 显示联系方式字段的错误提示 */}
    {errors.contact && <p className="error-text">{errors.contact.message}</p>}
  </div>

  <button type="submit">提交表单</button>
</form>

几个关键注意点要记牢

  • 必须用yupResolver:React Hook Form本身不认识Yup的schema,所以一定要通过@hookform/resolvers里的yupResolver把schema和表单关联起来,不然动态校验完全不会生效。
  • 类型切换时要reset表单:因为不同类型对应的字段结构或默认值可能不同,调用reset可以让表单重新加载对应类型的默认值,同时触发Yup.lazy重新生成新的校验规则,保证校验逻辑和当前类型匹配。
  • 字段名要完全对应:schema里定义的字段名(比如type、contact)必须和表单控件的name属性(通过register注册的)完全一致,不然校验规则找不到对应的字段。

拓展场景

如果你的场景更复杂,比如不同type对应完全不同的字段组(比如邮箱类型需要填邮箱验证码,手机号类型需要填短信验证码),只需要在getSchema里对应分支添加这些字段的校验规则,同时在UI里根据selectedType条件渲染对应的字段即可,Yup.lazy会自动识别当前表单值并应用对应的校验。

备注:内容来源于stack exchange,提问作者Mediator

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 16:28:02