基于表单外变量动态修改Yup字段验证规则的实现方法
React Hook Form + Yup 实现外部状态驱动的动态验证规则
问题背景
当前使用React Hook Form结合Yup做表单验证,静态Schema运行正常,但需要根据表单外的状态变量(如useState维护的max)动态修改字段的验证规则——具体是给number字段添加最大值限制,同时兼容复杂Schema和useFieldArray动态表单场景。
现有核心代码片段:
Yup 静态Schema
const schema = yup.object().shape({ ruleType: yup.string().required(errorMessages.required), address: yup.string().required(errorMessages.required), number: yup .string() .nullable() .required(errorMessages.required) .typeError(errorMessages.required) .when(['ruleType'], { is: (ruleType: string) => ruleType === 'collection', then: yup.string().nullable().test({ name: 'min', message: errorMessages.minNumber, test: (value) => +value > 0, }), otherwise: yup.string().nullable().test({ name: 'min', message: 'Must be more than 0', test: (value) => +value > 0 + 10 ** -18, }), }), });
React Hook Form 配置
const methods = useForm({ defaultValues, shouldFocusError: false, resolver: yupResolver(schema), });
外部状态变量
const [max, setMax] = useState(0);
可行解决方案
方法1:闭包直接引用外部状态 + 手动触发验证
无需重构整个Schema,直接在number字段的验证规则中通过闭包引用max,再在max变化时触发字段重验证。
// 修改后的Schema,新增max验证规则 const schema = yup.object().shape({ ruleType: yup.string().required(errorMessages.required), address: yup.string().required(errorMessages.required), number: yup .string() .nullable() .required(errorMessages.required) .typeError(errorMessages.required) .when(['ruleType'], { is: (ruleType: string) => ruleType === 'collection', then: yup.string().nullable().test({ name: 'min', message: errorMessages.minNumber, test: (value) => +value > 0, }), otherwise: yup.string().nullable().test({ name: 'min', message: 'Must be more than 0', test: (value) => +value > 0 + 10 ** -18, }), }) // 新增max验证,直接引用外部max状态 .test({ name: 'max-limit', message: `数值不能超过${max}`, test: (value) => { if (!value) return true; // 空值由required规则处理 return +value <= max; }, }), }); const methods = useForm({ defaultValues, shouldFocusError: false, resolver: yupResolver(schema), }); // 当max变化时,触发number字段的重新验证 useEffect(() => { methods.trigger('number'); }, [max, methods]);
方法2:动态生成Schema(适合复杂场景)
当Schema本身依赖多个外部状态时,用useMemo动态生成Schema,确保每次状态变化时Schema同步更新,再配合React Hook Form的动态resolver。
// 用useMemo生成依赖max的动态Schema const schema = useMemo(() => yup.object().shape({ ruleType: yup.string().required(errorMessages.required), address: yup.string().required(errorMessages.required), number: yup .string() .nullable() .required(errorMessages.required) .typeError(errorMessages.required) .when(['ruleType'], { is: (ruleType: string) => ruleType === 'collection', then: yup.string().nullable() .test({ name: 'min', message: errorMessages.minNumber, test: (v) => +v > 0 }) .test({ name: 'max', message: `不能超过${max}`, test: (v) => !v || +v <= max }), otherwise: yup.string().nullable() .test({ name: 'min', message: 'Must be more than 0', test: (v) => +v > 0 + 10 ** -18 }) .test({ name: 'max', message: `不能超过${max}`, test: (v) => !v || +v <= max }), }), }), [max, errorMessages]); // 用useCallback包裹resolver,确保拿到最新的Schema const resolver = useCallback( (values, context) => yupResolver(schema)(values, context), [schema] ); const methods = useForm({ defaultValues, shouldFocusError: false, resolver, }); // max变化时触发验证 useEffect(() => { methods.trigger('number'); }, [max, methods]);
方法3:兼容useFieldArray动态表单
如果使用useFieldArray管理动态字段,只需在数组字段的验证规则中引用max,并在max变化时触发所有动态字段的验证。
// 动态Schema示例(包含useFieldArray字段) const schema = useMemo(() => yup.object().shape({ // 其他固定字段... items: yup.array().of( yup.object().shape({ number: yup .string() .nullable() .required(errorMessages.required) .typeError(errorMessages.required) .test({ name: 'max', message: `不能超过${max}`, test: (value) => !value || +value <= max, }), // 其他动态字段验证规则... }) ), }), [max, errorMessages]); // 获取useFieldArray实例 const { fields, append, remove } = useFieldArray({ control: methods.control, name: 'items', }); // max变化时,触发所有动态number字段的验证 useEffect(() => { methods.trigger('items.*.number'); }, [max, methods]);
关键注意事项
- 空值处理:验证数字前先判断值是否为空,避免转换时出错,空值校验交给
required规则。 - 验证触发时机:外部状态变化时必须手动调用
trigger,否则React Hook Form不会自动重新验证。 - 性能优化:动态生成Schema时用
useMemo依赖状态变量,避免不必要的Schema重生成;resolver用useCallback包裹,减少组件重渲染。
内容的提问来源于stack exchange,提问作者Arad
相关产品推荐
相关产品推荐

