react-hook-form配置valueAsNumber后校验失效及Float类型报错问题
问题描述
提交表单值到后端时收到报错:Expected type Float. Float cannot represent non numeric value: "33"
技术栈为react-hook-form,基于自定义Input组件做表单扩展,原有配置代码如下:
<Input label="Cost" min={0} placeholder="Enter Amount" name="cost" type="number" register={register} required={{ required: 'Please enter a number', valueAsNumber: true, pattern: { value: currencyRegex, message: 'Please enter a valid number', }, }} error={errors.cost && <Error message={errors.cost.message} />} />
存在矛盾现象:
- 开启
valueAsNumber: true配置时,表单允许输入负数、特殊字符等不符合规则的内容,正则校验不生效 - 移除该配置后正则校验可正常生效,但提交时会触发上述Float类型不匹配报错
问题根因
- 配置写法错误:
valueAsNumber是register方法的一级配置项,和required、pattern平级,当前代码把它嵌套在校验规则内部,react-hook-form无法正确识别这个配置。 - 校验逻辑冲突:
pattern校验仅对字符串类型值生效,开启valueAsNumber后表单拿到的值是数字类型,正则匹配直接失效;另外原生type="number"输入框默认允许输入e/+/-这类特殊字符,这些字符会被Number()转成合法数字或者NaN,绕过正则校验。 - 关闭
valueAsNumber后,表单字段存的始终是字符串格式的数字,后端接收Float类型参数时识别到字符串值,就会抛出类型不匹配错误。
修复方案
二选一即可,推荐第一种:
方案1:修正配置+替换校验方式(最优)
- 调整配置层级,把
valueAsNumber放到规则对象的一级位置,不要把所有规则都塞到required属性里,如果自定义Input是把规则透传给register,建议统一用rules传参:
<Input label="Cost" min={0} placeholder="Enter Amount" name="cost" type="number" register={register} rules={{ required: 'Please enter a number', valueAsNumber: true, // 一级配置,和required平级 min: { value: 0, message: 'Cost cannot be less than 0' }, // 用validate代替pattern做校验,手动把数字转成字符串再匹配正则 validate: (val) => { if (Number.isNaN(val)) return 'Please enter a valid number' return currencyRegex.test(String(val)) || 'Please enter a valid number' } }} error={errors.cost && <Error message={errors.cost.message} />} />
对应的自定义Input内部,要修正register的参数传递逻辑:
// 自定义Input内部逻辑示例 const Input = ({ name, register, rules, ...rest }) => { // 把rules作为register的第二个参数传入,不要只传required字段 const { ref, ...fieldRest } = register(name, rules) return <input ref={ref} {...fieldRest} {...rest} /> }
- (可选体验优化)如果要直接禁止用户输入非法字符,可以在Input组件内加键盘事件拦截:
const handleKeyDown = (e) => { // 拦截e、E、+、-这些number输入框默认允许但不符合金额要求的按键 if (['e', 'E', '+', '-'].includes(e.key)) { e.preventDefault() } }
方案2:保留原有校验,提交前手动转类型
如果不想改动现有校验逻辑,可以直接移除valueAsNumber配置,让正则正常生效,在表单提交回调里手动把cost字段转成数字类型再传给后端:
const onSubmit = (formData) => { const payload = { ...formData, cost: Number(formData.cost) } // 调用后端接口传payload即可,不会触发Float类型报错 }
内容的提问来源于stack exchange,提问作者Faziki
相关产品推荐
相关产品推荐

