Yup实现相互依赖输入字段验证的问题求助
解决相互依赖字段的Yup+React Hook Form验证问题
需求:两个表单字段可均为空,但若其中一个已填写,另一个也必须填写,且两者都必须是100-999之间的数字。交互要求:填写一个字段时,另一个显示必填错误;清空一个字段且另一个也为空时,移除错误提示。
你的代码存在几个核心问题导致验证逻辑不生效:
- Yup when方法参数误用:
when回调里的val是对应字段的当前值(字符串类型),不是数组,原代码中val?.[0]?.length的判断完全错误 - field2Validation漏写return:条件满足时没有返回验证规则,导致无论field1是否有值,field2始终被标记为非必填
- test函数参数重名:内层test的参数名和外层when的
val重复,导致验证逻辑判断错误 - 循环依赖配置冗余:不需要双向配置
[['field1', 'field2'], ['field2', 'field1']],单一[['field1', 'field2']]即可让Yup正确处理双向依赖
以下是修复后的完整代码,附带关键逻辑说明:
import { useForm, FormProvider } from 'react-hook-form'; import { useYupValidationResolver } from '@hookform/resolvers/yup'; import * as yup from 'yup'; const ERROR_MESSAGE = 'Enter a number between 100 and 999'; // 复用数字验证规则,避免重复代码 const numberValidation = yup .string() .required(ERROR_MESSAGE) .test( 'validNumber', ERROR_MESSAGE, (value) => !isNaN(Number(value)) && Number(value) >= 100 && Number(value) <= 999 ); const validationSchema = yup.object().shape({ field1: yup.string().when('field2', { // 当field2有非空输入时,field1必须满足必填+数字规则 is: (val) => val?.trim().length > 0, then: () => numberValidation, // 当field2为空时,field1可选,但如果有输入仍需符合数字规则 otherwise: () => yup.string().test( 'validNumberOptional', ERROR_MESSAGE, (value) => !value || (!isNaN(Number(value)) && Number(value) >= 100 && Number(value) <= 999) ) }), field2: yup.string().when('field1', { is: (val) => val?.trim().length > 0, then: () => numberValidation, otherwise: () => yup.string().test( 'validNumberOptional', ERROR_MESSAGE, (value) => !value || (!isNaN(Number(value)) && Number(value) >= 100 && Number(value) <= 999) ) }) }, [['field1', 'field2']]); // 声明循环依赖,确保Yup正确处理双向验证 export const MyForm = () => { const formMethods = useForm({ resolver: useYupValidationResolver(validationSchema), mode: 'onChange' // 改为onChange实时触发验证,更贴合交互需求 }); const { handleSubmit, register, formState } = formMethods; const { errors } = formState; const onSubmit = (submitData) => { console.log(submitData); }; return ( <FormProvider {...formMethods}> <form onSubmit={handleSubmit(onSubmit)}> <div> <input type="text" {...register('field1')} placeholder="Field 1 (100-999)" /> {errors.field1 && <span style={{ color: 'red' }}>{errors.field1.message}</span>} </div> <div> <input type="text" {...register('field2')} placeholder="Field 2 (100-999)" /> {errors.field2 && <span style={{ color: 'red' }}>{errors.field2.message}</span>} </div> <button type="submit">Submit</button> </form> </FormProvider> ); };
关键修复说明
- 复用验证规则:将数字验证逻辑抽离为独立变量,减少代码冗余
- 准确的非空判断:用
val?.trim().length > 0判断字段是否有有效输入(过滤空格) - 补全可选字段的验证:即使字段可选,只要有输入就必须符合数字规则,避免无效内容提交
- 简化循环依赖声明:仅需声明一次
[['field1', 'field2']],Yup会自动处理双向依赖关系 - 安全的错误提示访问:用
errors.field1 && ...避免字段无错误时触发Cannot read property 'message' of undefined - 优化验证触发时机:将
mode改为onChange,实时响应输入变化,符合用户预期的交互体验
内容的提问来源于stack exchange,提问作者noblerare
相关产品推荐
相关产品推荐

