基于React-Hook-Form的数字输入组件优化:移除useState/useEffect
背景
- 想要开发一个数字输入框(Number Input)
- 该自定义输入框仅接受数字输入,并具备字符长度限制
- 需配合React-Hook-Form使用
import React, { useEffect, useState } from "react"; type NumberInputProps = { _maxLength?: number; name: string; className?: string; onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void; placeholder?: string; value?: string; }; const NumberInput = ({ _maxLength, className, name, onChange: handleChange, placeholder, value }: NumberInputProps) => { const [val, setVal] = useState(value); useEffect(() => { setVal(value); }, [value]); const handleInput = (e: React.ChangeEvent<HTMLInputElement>) => { alert("handleInput !"); let value = e.target.value; if (_maxLength && value.length >= _maxLength) { value = value.slice(0, _maxLength); } setVal(value); handleChange?.(e); }; const cn = `number-input ${className}`; return ( <input name={name} className={cn} type="number" onInput={handleInput} placeholder={placeholder} value={val} /> ); }; export default NumberInput;
问题
当前组件为受控组件,但不想使用useState和useEffect,因为这会让代码变得冗长。是否存在更简短、更直观的实现方式?
可以利用React-Hook-Form的Controller组件接管状态管理,同时简化输入处理逻辑,完全去掉本地状态相关代码:
优化后的数字输入组件
import React from "react"; type NumberInputProps = { _maxLength?: number; name: string; className?: string; onChange: (value: string) => void; placeholder?: string; value?: string; }; const NumberInput = ({ _maxLength, className, name, onChange, placeholder, value }: NumberInputProps) => { const handleInput = (e: React.ChangeEvent<HTMLInputElement>) => { // 过滤所有非数字字符 const filteredValue = e.target.value.replace(/[^0-9]/g, ""); // 按限制截取长度 const finalValue = _maxLength ? filteredValue.slice(0, _maxLength) : filteredValue; // 将处理后的值传递给React-Hook-Form onChange(finalValue); }; const cn = `number-input ${className || ""}`; return ( <input name={name} className={cn} type="text" // 改用text类型避免number输入的默认干扰(如e、正负号) onInput={handleInput} placeholder={placeholder} value={value || ""} /> ); }; export default NumberInput;
配合React-Hook-Form使用的示例
import { Controller, useForm } from "react-hook-form"; import NumberInput from "./NumberInput"; const DemoForm = () => { const { control, handleSubmit } = useForm(); const onSubmit = (formData: any) => { console.log("提交的数据:", formData); }; return ( <form onSubmit={handleSubmit(onSubmit)}> <Controller name="mobile" control={control} defaultValue="" render={({ field: { onChange, value } }) => ( <NumberInput name="mobile" _maxLength={11} onChange={onChange} value={value} placeholder="请输入手机号" /> )} /> <button type="submit">提交</button> </form> ); }; export default DemoForm;
优化说明
- 移除了
useState和useEffect,直接依赖React-Hook-Form传入的value和onChange,完全由表单库管理状态 - 把
type="number"改为type="text",配合正则过滤非数字字符,避免number类型输入允许的特殊字符(如e、+、-)带来的问题 - 输入处理逻辑合并为一步:过滤+长度限制,代码更直观简洁
- 组件仅专注于输入校验和格式处理,状态管理完全交给React-Hook-Form,符合单一职责原则
内容的提问来源于stack exchange,提问作者Byeongin Yoon
相关产品推荐
相关产品推荐

