如何使用Yup实现required_one_of条件校验 解决循环依赖报错
问题背景
现有基于JSON Schema驱动的表单,配置结构示例如下:
{ "json_schema": { "properties": { "analyst_time": { "description": "Analyst Task Time, in minutes", "is_duration_field": true, "mouseover_help": "...", "order": 11, "required_positive": true, "type": "integer" } }, "required_one_of": [ ["analyst_time", "instrument_time"] ] } }
properties对象存放所有字段的定义,表单渲染、单字段基础校验规则都基于该对象生成- 需要扩展的校验规则:针对
required_one_of数组中定义的字段组增加条件校验,规则为同组字段在没有任何一个被填充有效值时,组内所有字段均为必填状态;只要组内有任意一个字段填入有效值,其余字段的必填限制自动解除
此前尝试通过yup.when方法实现该需求,但由于同组字段间形成循环依赖关系直接触发报错,报错截图如下:
实现方案
不要使用字段两两互相绑定when的写法,这类写法必然会触发Yup的循环依赖检测。把同组字段的校验逻辑抽离到共同的父级节点做统一判断即可完全规避这个问题,具体实现分两种场景:
同组字段归属同一对象节点
如果组内所有字段都在同一个对象层级下,直接给该父对象添加自定义test校验,不需要在单个字段上编写互相依赖的when逻辑:
import * as yup from 'yup'; // 直接从schema配置中读取required_one_of分组规则 const requiredOneOfGroups = json_schema.required_one_of; // 先基于properties生成单字段的基础校验规则 const fieldValidators = Object.entries(json_schema.properties).reduce((collected, [fieldKey, fieldConfig]) => { let validator = yup.number().integer(); if (fieldConfig.required_positive) { validator = validator.positive(); } collected[fieldKey] = validator; return collected; }, {}); // 在父级表单对象上添加分组统一校验 const formValidator = yup.object({ ...fieldValidators }).test( 'required-one-of-check', '请至少填写组内一个字段', (formValues) => { for (const group of requiredOneOfGroups) { // 判断当前组是否已有填充值,可根据业务调整“已填充”的判定标准 const hasValidValue = group.some(fieldKey => { const val = formValues[fieldKey]; return val !== null && val !== undefined && val !== ''; }); if (!hasValidValue) { // 给组内所有字段挂载校验错误,和单字段必填的报错表现完全一致 throw new yup.ValidationError( group.map(fieldKey => new yup.ValidationError( '当前组需至少填写一个字段', formValues[fieldKey], fieldKey )) ); } } return true; } );
这种写法完全规避了单字段间的循环引用问题,还可以直接复用JSON Schema中的required_one_of配置,不需要为每个分组单独编写重复逻辑。
同组字段跨嵌套层级
如果组内字段分布在表单的不同嵌套路径下,把上述统一校验逻辑挂载到最外层的根表单Schema上即可,仅需要调整取值逻辑,支持按嵌套路径读取对应字段值,核心校验判断逻辑完全不变。
适配提示:如果搭配react-hook-form的Yup resolver使用,上述父级校验抛出的带字段名的错误会自动映射到对应表单项,和单字段直接配置
required()的错误展示效果没有区别,不需要额外做错误分发处理。
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

