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

如何在react-hook-form中向验证函数传递参数

问题描述

我正在为应用评估react-hook-form库,由于底层依赖MUI,必须使用Controller组件。希望创建通用验证规则文件供多表单复用,但需要向validate选项的函数传递动态参数:

  • minMaxLen需接收动态min和max值
  • isValueEqual需要对比其他字段的值,但react-hook-form的validate回调默认仅传入当前字段值

附上现有代码:

表单组件

import React, { BaseSyntheticEvent } from 'react';
import { Controller, useForm  } from 'react-hook-form';
import { ruleSet } from '../../utils/validation/rules';
import { Validate, FieldValues } from 'react-hook-form';

export const ReactHookFormSample = (): JSX.Element => {
    const { handleSubmit, control, formState: { errors }, getValues, register } = useForm({
        mode: "onChange"
    });

    const onSubmit = (data: any, e: BaseSyntheticEvent) =>  {
        console.log('hook form', data);
    }

    const onError = (errors: any, e: BaseSyntheticEvent) =>  {
        console.log('When errors form', errors);
    }

    const getError = (field: FieldValues) => {
        let errorStr =  '';
        let inputName = field?.name;
        if (errors && errors?.[inputName]) {
            errorStr = errors?.[inputName]?.message;
        }
        return errorStr;
    }

    return (
        <>
            <h3>Sample React hook form</h3>
            <form onSubmit={handleSubmit(onSubmit, onError)}>
                <Controller
                    name="user_name"
                    control={control}
                    rules={{ validate: {isRequired: ruleSet.isRequired, minLen: ruleSet.minLen, maxLen: ruleSet.maxLen } }}
                    render={({ field, fieldState: {error}, formState }) => 
                            <MyTextField placeholder="Enter user name here" error={!!error} {...field} />
                    }
                />
                <br/>
                <Controller
                    name="password"
                    control={control}
                    rules={{ validate: {isRequired: ruleSet.isRequired, minLen: ruleSet.minLen, maxLen: ruleSet.maxLen } }}
                    render={({ field, fieldState: {error}, formState }) => 

                            <MyTextField placeholder="Enter password here" error={!!error} {...field} />
                    }
                />
                <br/>
                <Controller
                    name="retype_password"
                    control={control}
                    rules={{ validate: {isRequired: ruleSet.isRequired, minMaxLen: ruleSet.minMaxLen, ruleSet.isValueEqual } }}
                    render={({ field, fieldState: {error}, formState }) => 
                            
                            <MyTextField placeholder="Retype password here" error={!!error} {...field} />
                    }
                />
                <br/>
                <MyButton color="primary" type="submit">
                    Submit Form
                </MyButton>
            </form>
        </>
    );
};

通用规则文件

export const isRequired = (value: string) => {
    return value ? true : 'This is a required input, can not escape';
}

export const maxLen = (value: string) => {
    return value.length <= 15 || 'Value cannot be more than 15 chars';
}

const minLen = (value: string) => {
    return value.length >= 7 || 'Value cannot be less than 7 chars';
}

const minMaxLen = (value: string) => { // 需要动态传入min和max参数
    return value.length >= <SomeMinValue> && value.length <= <SomeMaxValue> || `Should be between ${SomeMinValue} and ${SomeMaxValue} chars`;
}

const isValueEqual = (value: string) => {
        const valOfFieldToBeComparedWith = getValues('password'); // 需动态传入要对比的字段名或getValues方法
        return value === valOfFieldToBeComparedWith || 'Password and retype password do not match';
    }


export const ruleSet = {
    isRequired,
    minLen,
    maxLen,
    minMaxLen,
    isValueEqual
}
解决方案

核心思路是让验证函数返回闭包,先接收动态参数,再返回实际的验证回调函数,这样就能在表单组件中灵活传入自定义参数。

1. 改造支持动态参数的验证函数

将minLen、maxLen、minMaxLen改为接收参数后返回验证函数的结构,提升通用性:

修改后的通用规则文件:

export const isRequired = (value: string) => {
    return value ? true : 'This is a required input, can not escape';
}

// 支持动态max值
export const maxLen = (max: number) => {
    return (value: string) => {
        return value.length <= max || `Value cannot be more than ${max} chars`;
    }
}

// 支持动态min值
export const minLen = (min: number) => {
    return (value: string) => {
        return value.length >= min || `Value cannot be less than ${min} chars`;
    }
}

// 支持动态min和max值
export const minMaxLen = (min: number, max: number) => {
    return (value: string) => {
        return value.length >= min && value.length <= max || `Should be between ${min} and ${max} chars`;
    }
}

// 支持动态传入要对比的字段名和getValues方法
export const isValueEqual = (targetField: string, getValues: () => any) => {
    return (value: string) => {
        const targetValue = getValues(targetField);
        return value === targetValue || `Value does not match ${targetField}`;
    }
}

export const ruleSet = {
    isRequired,
    minLen,
    maxLen,
    minMaxLen,
    isValueEqual
}

2. 表单组件中使用改造后的验证函数

在Controller的rules中,调用验证函数并传入所需的动态参数:

修改后的表单组件关键部分:

// ... 其他代码不变
return (
    <>
        <h3>Sample React hook form</h3>
        <form onSubmit={handleSubmit(onSubmit, onError)}>
            <Controller
                name="user_name"
                control={control}
                rules={{ 
                    validate: {
                        isRequired: ruleSet.isRequired,
                        minLen: ruleSet.minLen(7), // 传入min=7
                        maxLen: ruleSet.maxLen(15) // 传入max=15
                    } 
                }}
                render={({ field, fieldState: {error}, formState }) => 
                        <MyTextField placeholder="Enter user name here" error={!!error} {...field} />
                }
            />
            <br/>
            <Controller
                name="password"
                control={control}
                rules={{ 
                    validate: {
                        isRequired: ruleSet.isRequired,
                        minLen: ruleSet.minLen(7),
                        maxLen: ruleSet.maxLen(15)
                    } 
                }}
                render={({ field, fieldState: {error}, formState }) => 
                        <MyTextField placeholder="Enter password here" error={!!error} {...field} />
                }
            />
            <br/>
            <Controller
                name="retype_password"
                control={control}
                rules={{ 
                    validate: {
                        isRequired: ruleSet.isRequired,
                        minMaxLen: ruleSet.minMaxLen(7,15), // 传入min=7,max=15
                        isValueEqual: ruleSet.isValueEqual('password', getValues) // 传入目标字段名和getValues方法
                    } 
                }}
                render={({ field, fieldState: {error}, formState }) => 
                        <MyTextField placeholder="Retype password here" error={!!error} {...field} />
                }
            />
            <br/>
            <MyButton color="primary" type="submit">
                Submit Form
            </MyButton>
        </form>
    </>
);

补充说明

  • 闭包的方式可以让验证函数在定义时捕获动态参数,在实际验证时使用这些参数进行判断
  • 跨字段验证时,通过传入getValues方法,验证函数可以灵活获取任意字段的值,不需要硬编码字段名
  • 所有验证函数保持了通用性,可在其他表单中重复调用,只需传入不同的参数即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 09:15:41