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

