在React项目中使用Formik管理表单状态时,数字输入框的范围校验是高频需求,通常我们会搭配Yup校验库来实现规则定义,两者配合可以高效完成各类表单校验逻辑。

Formik与Yup的基本配合逻辑
Formik是React生态中常用的表单管理库,负责表单状态维护、值变更监听、提交处理等核心能力,而Yup是专门用于JavaScript对象校验的库,支持链式定义校验规则,两者可以通过Formik的validationSchema属性直接结合使用。
首先我们需要安装两个依赖:
npm install formik yup
Yup内置方法实现数字范围校验
Yup提供了number类型的定义方法,以及min和max两个内置方法,可以直接用来限制数字的最小值和最大值,这是实现数字输入框范围校验最简单的方式。
基础范围校验示例
下面是一个完整的Formik表单示例,包含一个数字输入框,要求输入值必须在1到100之间:
import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';
// 定义校验规则
const validationSchema = Yup.object({
score: Yup.number()
.required('请输入分数')
.min(1, '分数不能小于1')
.max(100, '分数不能大于100')
.typeError('请输入有效的数字')
});
const NumberRangeForm = () => {
return (
<Formik
initialValues={{ score: '' }}
validationSchema={validationSchema}
onSubmit={(values) => {
console.log('提交的值:', values);
}}
>
{({ errors, touched }) => (
<Form>
<div>
<label htmlFor="score">分数:</label>
<Field
name="score"
type="number"
id="score"
/>
<ErrorMessage
name="score"
component="div"
style={{ color: 'red' }}
/>
</div>
<button type="submit">提交</button>
</Form>
)}
</Formik>
);
};
export default NumberRangeForm;
上面的代码中,Yup.number()首先指定该字段为数字类型,required定义必填规则,min(1, '提示信息')限制最小值不能小于1,max(100, '提示信息')限制最大值不能大于100,typeError用来处理用户输入非数字内容时的错误提示。
自定义校验规则处理特殊场景
如果遇到Yup内置方法无法满足的场景,比如需要限制数字必须是5的倍数,或者范围需要根据其他字段的值动态变化,我们可以使用test方法自定义校验逻辑。
动态范围校验示例
下面的示例中,数字输入框的最大值会根据另一个输入框的值动态变化:
import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';
const dynamicValidationSchema = Yup.object({
maxLimit: Yup.number()
.required('请输入最大限制值')
.min(1, '限制值不能小于1'),
targetNum: Yup.number()
.required('请输入目标数字')
.min(1, '数字不能小于1')
.test(
'max-dynamic',
'目标数字不能超过最大限制值',
function (value) {
const { maxLimit } = this.parent;
// 如果最大限制值不存在或者目标值不存在,直接返回true
if (!maxLimit || !value) return true;
return value <= maxLimit;
}
)
.typeError('请输入有效的数字')
});
const DynamicRangeForm = () => {
return (
<Formik
initialValues={{ maxLimit: '', targetNum: '' }}
validationSchema={dynamicValidationSchema}
onSubmit={(values) => {
console.log('提交的值:', values);
}}
>
{() => (
<Form>
<div>
<label htmlFor="maxLimit">最大限制值:</label>
<Field
name="maxLimit"
type="number"
id="maxLimit"
/>
<ErrorMessage
name="maxLimit"
component="div"
style={{ color: 'red' }}
/>
</div>
<div>
<label htmlFor="targetNum">目标数字:</label>
<Field
name="targetNum"
type="number"
id="targetNum"
/>
<ErrorMessage
name="targetNum"
component="div"
style={{ color: 'red' }}
/>
</div>
<button type="submit">提交</button>
</Form>
)}
</Formik>
);
};
export default DynamicRangeForm;
这里的test方法接收三个参数,第一个是校验名称,第二个是错误提示信息,第三个是校验函数,函数中的this.parent可以获取整个表单的所有值,从而实现跨字段的动态校验逻辑。
常见问题与注意事项
- 数字输入框如果设置
type="number",浏览器会自带部分输入限制,但Yup的校验仍然需要配置,因为用户可能通过粘贴等方式输入不符合规则的值。 - 如果初始值是空字符串,Yup的
number()类型会认为空值不是有效数字,所以需要搭配required规则,或者在typeError中处理空值的提示。 - 自定义
test校验时,如果校验逻辑依赖其他字段,需要注意字段的初始值可能为空的场景,避免校验函数报错。