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

React IpAddressInput组件问题:删除数字意外影响其他网段

解决react-text-mask实现IPv4输入框的删除异常问题

你的问题核心来自三个关键缺陷:

  • 实时验证逻辑过于严苛:只有完全符合IPv4标准的输入才允许更新状态,导致删除过程中的中间状态(如123.4_6.789.222这类不完整网段)无法被组件捕获,mask只能自行处理输入引发跨段移位。
  • keepCharPositions设置错误:关闭该属性后,删除字符时mask会重新排列整个输入内容,直接导致相邻网段的字符被错误移动。
  • 静态掩码无法适配IPv4动态规则:固定的掩码模板没有考虑网段0-255的范围限制,也未处理删除时的网段边界保护。

修复步骤

  1. 放宽实时验证,仅做基础分段检查
    实时输入时只验证内容为数字和点号、点号数量不超过3,把严格的IPv4验证放到失焦或提交环节。
  2. 开启keepCharPositions
    保留字符位置,让删除操作仅影响当前网段,不会波及其他网段。
  3. 使用动态掩码函数
    根据当前输入的网段内容自动调整掩码规则,处理0-255的范围限制,同时支持输入满3位后自动跳段、删除时自动回退。
  4. 添加失焦格式化逻辑
    输入框失焦时,自动将不完整的IP修正为合法的IPv4格式。

修改后的完整代码

import React, { useState, useEffect, useCallback } from 'react';
import MaskedInput from 'react-text-mask';

const IpAddressInput = ({ initialValue = '', onChange, onBlur }) => {
    const [value, setValue] = useState(initialValue);

    // 动态生成适配IPv4的掩码
    const createIPMask = useCallback((rawValue) => {
        const mask = [];
        const segments = rawValue.split('.').filter(Boolean);

        segments.forEach((segment, index) => {
            const num = parseInt(segment, 10);
            if (!isNaN(num)) {
                // 根据当前网段数值调整掩码规则,确保不超过255
                if (num > 255) {
                    mask.push(/[0-2]/, /[0-5]/, /[0-5]/);
                } else if (num >= 100) {
                    mask.push(/\d/, /\d/, /\d/);
                } else if (num >= 10) {
                    mask.push(/\d/, /\d/, /\d?/);
                } else {
                    mask.push(/\d/, /\d?/, /\d?/);
                }
            } else {
                mask.push(/\d/, /\d?/, /\d?/);
            }
            // 非最后一个网段添加点号分隔符
            if (index < 3) {
                mask.push('.');
            }
        });

        // 补全剩余网段和点号,确保掩码长度符合IPv4结构
        while (mask.length < 15) {
            const segmentIndex = Math.floor(mask.length / 4);
            if (segmentIndex < 4) {
                mask.length % 4 === 3 ? mask.push('.') : mask.push(/\d/, /\d?/, /\d?/);
            }
        }

        return mask.slice(0, 15);
    }, []);

    // 实时输入处理:允许中间状态,仅做基础合法性检查
    const handleChange = useCallback((event) => {
        const newValue = event.target.value;
        const isValid = /^(\d{1,3}\.?){0,4}$/.test(newValue.replace(/\.$/, ''));
        if (isValid) {
            setValue(newValue);
            onChange(newValue);
        }
    }, [onChange]);

    // 失焦时格式化IP为合法IPv4格式
    const handleBlur = useCallback((event) => {
        let formattedValue = event.target.value.trim();
        const segments = formattedValue.split('.').map(seg => {
            // 去除前导零(单独的0保留)
            const trimmed = seg.replace(/^0+(?=\d)/, '');
            // 空网段默认设为0
            if (!trimmed) return '0';
            // 限制数值在0-255之间
            const num = parseInt(trimmed, 10);
            return Math.max(0, Math.min(255, num)).toString();
        });

        // 补全或截断到4个网段
        const finalSegments = [...segments.slice(0, 4)];
        while (finalSegments.length < 4) {
            finalSegments.push('0');
        }
        const finalIP = finalSegments.join('.');
        
        setValue(finalIP);
        onChange(finalIP);
        onBlur && onBlur(finalIP);
    }, [onChange, onBlur]);

    useEffect(() => {
        setValue(initialValue);
    }, [initialValue]);

    return (
        <MaskedInput
            mask={createIPMask}
            value={value}
            onChange={handleChange}
            onBlur={handleBlur}
            placeholder="Enter IPv4 address"
            keepCharPositions={true} // 开启字符位置保留,防止删除移位
            guide={true} // 显示引导字符,提升输入体验
        />
    );
};

export default IpAddressInput;

// 父组件调用示例
function Settings() {
    const [ipAddress, setIpAddress] = useState('');

    const handleIpAddressChange = (value) => {
        console.log('IP Address changed:', value);
        setIpAddress(value);
    };

    const handleIpBlur = (value) => {
        console.log('IP Address formatted on blur:', value);
    };

    return (
        <div className="ControlContainer">
            <h3 style={{ marginBottom: '13px' }}>IP Address</h3>
            <IpAddressInput 
                initialValue={ipAddress} 
                onChange={handleIpAddressChange}
                onBlur={handleIpBlur}
            />
        </div>
    );
}

export default Settings;

关键修改说明

  • 动态掩码:根据当前输入的网段数值自动调整规则,既保证输入不超过255,又保留输入灵活性。
  • 宽松实时验证:允许用户输入中间状态,避免mask因组件状态不更新而出现异常移位。
  • 字符位置保留:彻底解决删除操作跨段影响其他网段的问题。
  • 失焦格式化:自动修正不完整IP,确保最终输出符合IPv4标准。

内容的提问来源于stack exchange,提问作者kannan srank

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 13:18:11