You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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;

优化说明

  1. 移除了useState和useEffect,直接依赖React-Hook-Form传入的value和onChange,完全由表单库管理状态
  2. 把type="number"改为type="text",配合正则过滤非数字字符,避免number类型输入允许的特殊字符(如e、+、-)带来的问题
  3. 输入处理逻辑合并为一步:过滤+长度限制,代码更直观简洁
  4. 组件仅专注于输入校验和格式处理,状态管理完全交给React-Hook-Form,符合单一职责原则

内容的提问来源于stack exchange,提问作者Byeongin Yoon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 20:33:37