如何避免Yup修改React Hook Form表单提交数据?数组至少1个true值的校验方案咨询
嘿,我之前在项目里也碰到过一模一样的问题——Yup的compact()这类转换方法会悄悄修改最终传给onSubmit的表单数据,导致后续处理逻辑出错。下面针对你的三个问题逐一给出实用的解决方案:
1. 是否能让Yup仅校验不修改原始数据?
可以,但有个关键限制:关闭转换后,你的compact()也会失效。
Yup的yupResolver默认会开启transform选项(这是Yupvalidate()方法的默认行为),所以校验过程中会执行数据转换。要关闭它,只需在useForm的resolver配置里添加validateOptions:
const { handleSubmit } = useForm({ resolver: yupResolver(schema, { validateOptions: { transform: false, // 禁用所有数据转换 }, }), });
不过这里要注意:关闭transform后,compact()不会再执行,你的min(1)校验会失效(因为数组里的false还在,compact()没把它们移除,数组长度始终是3,永远满足min(1))。所以这个方案只能单纯保留原始数据,没法同时满足你的校验需求,得结合其他方式用。
2. 用了compact()后,如何获取完整原始数据?
如果不想放弃compact()的校验逻辑,你可以直接从React Hook Form中获取未被Yup修改的原始表单值,而不用onSubmit参数里的转换后数据。
React Hook Form提供了getValues()方法,可以直接获取表单的原始值:
const { handleSubmit, getValues } = useForm({resolver: yupResolver(schema)}); const onSubmit = () => { // 直接获取原始的roles数组,不受Yup转换影响 const originalRoles = getValues('roles'); console.log(originalRoles); // 输出 [true, false, true] const selectedRoles = roles.filter((_r, i) => originalRoles[i]); console.log(selectedRoles); // 正确输出 ["foo", "baz"] };
这种方法的好处是既保留了Yup的compact()+min(1)校验,又能拿到原始数据做后续处理,缺点是需要额外调用getValues(),不能直接用onSubmit的参数。
3. 验证数组中至少N个true且不修改原始数据的最优方案
这是我最推荐的方案——用Yup的test()方法自定义校验逻辑,完全不触碰原始数组。
你可以直接统计数组中true的数量,而不用compact()移除false:
const schema = yup .object({ roles: yup .array() .test( 'min-true-count', // 校验规则的唯一标识(可选) 'A user must have at least one role assigned', // 错误提示 (value) => { // 处理数组为空的情况 if (!Array.isArray(value)) return false; // 统计数组中true的数量,至少1个 return value.filter(Boolean).length >= 1; } ), }) .required();
这个方案的优势非常明显:
- 完全不会修改原始表单数据,
onSubmit里拿到的就是用户提交的[true, false, true] - 校验逻辑更灵活,比如要校验至少2个选中项,只需把
>=1改成>=2 - 逻辑清晰,其他人看代码一眼就能明白你要校验什么
总结
如果你的核心需求是保留原始数据+正确校验,优先用问题3的自定义test()方案;如果一定要用compact(),可以用问题2的getValues()获取原始数据;问题1的方案只适合不需要compact()这类转换的场景。
内容的提问来源于stack exchange,提问作者fredrivett

