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
相关产品推荐
相关产品推荐

