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

React动态添加输入框至数组报错求助:e.preventDefault未定义

React动态输入框更新数组状态报错修复

问题核心

尝试通过动态创建输入框收集多个值到resistances数组时,遭遇e.preventDefault is not a function等错误,同时当前逻辑无法实现多输入值的正确收集。

现有代码问题

1. 滥用e.preventDefault()

input事件的事件对象不存在preventDefault()方法,仅表单提交、锚点跳转这类有默认行为的事件才需调用该方法,你在handleChange和动态输入框的input事件中调用此方法直接引发报错。

2. 手动DOM操作违背React理念

用document.createElement和appendChild手动创建的DOM元素,React无法追踪其状态与事件绑定,导致触发事件时e.target出现undefined。

3. 状态更新逻辑错误

当前处理resist输入时,直接将resistances数组替换为[e.target.value],会覆盖之前所有输入值,根本无法实现多值收集。

4. 事件传参错误

动态输入框的input事件调用handleChange时传了两个参数,但handleChange仅接收一个,导致原事件对象被覆盖,进而出现e.target undefined的问题。

修复方案(符合React最佳实践)

放弃手动DOM操作,改用React状态管理动态输入框的数量和值,以下是完整修复代码:

import { useState } from 'react';

function CombatForm() {
    // 初始化状态,resistances默认包含一个空字符串,对应初始输入框
    const [combat, setCombat] = useState({
        ac:'',
        proBonus:'',
        init:'',
        speed:'',
        atkPerRound:'',
        resistances: ['']
    });

    // 处理普通属性的输入变化
    const handleGeneralInput = (e) => {
        const { name, value } = e.target;
        // 使用函数式更新确保基于最新状态修改
        setCombat(prevState => ({
            ...prevState,
            [name]: value
        }));
    };

    // 处理单个电阻输入框的变化,通过索引定位修改数组元素
    const handleResistInput = (index, newValue) => {
        setCombat(prevState => {
            const updatedResistances = [...prevState.resistances];
            updatedResistances[index] = newValue;
            return {
                ...prevState,
                resistances: updatedResistances
            };
        });
    };

    // 添加新的电阻输入框,在数组末尾追加空字符串
    const addNewResist = () => {
        setCombat(prevState => ({
            ...prevState,
            resistances: [...prevState.resistances, '']
        }));
    };

    // 移除指定索引的电阻输入框(至少保留一个)
    const removeResist = (index) => {
        if (combat.resistances.length <= 1) return;
        setCombat(prevState => ({
            ...prevState,
            resistances: prevState.resistances.filter((_, i) => i !== index)
        }));
    };

    return (
        <div className="combat-form">
            {/* 普通属性输入区域 */}
            <div className="form-group">
                <label htmlFor="ac">AC</label>
                <input
                    type="text"
                    id="ac"
                    name="ac"
                    value={combat.ac}
                    onChange={handleGeneralInput}
                    placeholder="输入AC值"
                />
            </div>
            <div className="form-group">
                <label htmlFor="proBonus">熟练加值</label>
                <input
                    type="text"
                    id="proBonus"
                    name="proBonus"
                    value={combat.proBonus}
                    onChange={handleGeneralInput}
                    placeholder="输入熟练加值"
                />
            </div>
            <div className="form-group">
                <label htmlFor="init">先攻值</label>
                <input
                    type="text"
                    id="init"
                    name="init"
                    value={combat.init}
                    onChange={handleGeneralInput}
                    placeholder="输入先攻值"
                />
            </div>
            <div className="form-group">
                <label htmlFor="speed">速度</label>
                <input
                    type="text"
                    id="speed"
                    name="speed"
                    value={combat.speed}
                    onChange={handleGeneralInput}
                    placeholder="输入速度"
                />
            </div>
            <div className="form-group">
                <label htmlFor="atkPerRound">每回合攻击次数</label>
                <input
                    type="text"
                    id="atkPerRound"
                    name="atkPerRound"
                    value={combat.atkPerRound}
                    onChange={handleGeneralInput}
                    placeholder="输入每回合攻击次数"
                />
            </div>

            {/* 电阻输入区域 */}
            <div className="form-group" id="resistList">
                <label>抗性</label>
                {combat.resistances.map((resistValue, index) => (
                    <div key={index} className="resist-input-row">
                        <input
                            type="text"
                            value={resistValue}
                            onChange={(e) => handleResistInput(index, e.target.value)}
                            placeholder="输入抗性类型"
                        />
                        {/* 只有当输入框数量大于1时显示移除按钮 */}
                        {combat.resistances.length > 1 && (
                            <button onClick={() => removeResist(index)}>移除</button>
                        )}
                    </div>
                ))}
                <button onClick={addNewResist}>添加抗性</button>
            </div>
        </div>
    );
}

export default CombatForm;

修复说明

  1. 用状态管理动态输入框:通过resistances数组的长度控制输入框数量,React自动渲染所有输入框,无需手动操作DOM,彻底避免事件绑定异常。
  2. 精准更新数组元素:每个输入框通过索引对应数组中的元素,修改时只更新对应位置的值,不会覆盖其他输入内容。
  3. 移除无效的preventDefault():仅在有默认行为的事件(如表单提交)中使用该方法,消除报错根源。
  4. 函数式状态更新:所有状态更新都使用prevState => ...的形式,确保每次修改都基于最新状态,避免闭包导致的状态不一致问题。
  5. 新增移除功能:提升用户体验,允许删除多余的输入框(至少保留一个)。

内容的提问来源于stack exchange,提问作者Christopher Dirksen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 04:50:25