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

基于表单外变量动态修改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]);

关键注意事项

  1. 空值处理:验证数字前先判断值是否为空,避免转换时出错,空值校验交给required规则。
  2. 验证触发时机:外部状态变化时必须手动调用trigger,否则React Hook Form不会自动重新验证。
  3. 性能优化:动态生成Schema时用useMemo依赖状态变量,避免不必要的Schema重生成;resolver用useCallback包裹,减少组件重渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 07:54:54