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

React中按索引删除useState数组元素及解决defaultValue更新问题

解决React中useState数组删除元素后Input defaultValue不更新的问题

问题场景

我通过useState维护学生列表数组:

const [students, setStudents] = useState([""])

将数组映射为Student组件:

{students.map((student, index) => (
  <Student 
    setStudents={setStudents} 
    students={students} 
    id={index} 
    key={index} 
    content={student} 
  />
))}

AddStudent组件可以正常添加新学生:

function AddStudent(props) {
    const {setStudents} = props;
    return (
        <button className="change-student add-student" onClick={() => {
            setStudents((students) => [...students, ""])
        }}>
            +
        </button>
    );
}

但RemoveStudent组件按索引删除元素时始终无法正常工作,尝试了多种写法都无效:

function RemoveStudent(props) {
    const {students, setStudents, id} = props;
    return (
        <button className="change-student remove-student" onClick={() => {
            let data = students;
            if(id > -1) {
                data.splice(id, 1);
            }
            console.log(data)
            // setStudents(data)
            // alternative:
            // setStudents(students.filter(index => index !== id)); // removes the last element in the list
            // doesn't work properly
        }}>
            -
        </button>
    )
}

问题分析

  1. 直接修改原数组:splice会直接篡改原students数组的引用,React依赖状态引用变化触发渲染,因此无法检测到状态更新。
  2. Filter逻辑错误:students.filter(index => index !== id)中,filter的第一个参数是数组元素而非索引,这段代码实际是删除值等于id的元素,而非对应索引的元素,导致删除结果不符合预期。
  3. Key复用导致的渲染异常:使用数组索引作为Student组件的key,删除元素后后续组件的key会前移复用,React会认为这些组件没有变化,不会重新渲染Student内部的Input组件——而defaultValue仅在组件挂载时生效,因此旧的defaultValue会被保留。

解决方案

1. 修正RemoveStudent的删除逻辑

采用不可变更新方式,基于前一次状态创建新数组,同时正确使用filter按索引删除:

function RemoveStudent(props) {
    const {setStudents, id} = props;
    return (
        <button className="change-student remove-student" onClick={() => {
            setStudents(prevStudents => prevStudents.filter((_, idx) => idx !== id));
        }}>
            -
        </button>
    )
}

这里通过prevStudents获取最新状态,避免闭包导致的状态过期问题,同时利用filter的第二个参数(索引)精准删除目标元素。

2. 刷新Input的defaultValue

由于defaultValue仅在组件首次挂载时赋值,要让它随状态更新,需要让React重新创建Input组件。核心方法是给Input设置唯一的key,确保元素变化时key也随之变化:

// Student组件内的Input示例
function Student({ content, id }) {
    return (
        <input 
            defaultValue={content} 
            key={`student-input-${id}-${content}`} 
            // 其他属性
        />
    );
}

进阶优化:使用唯一ID代替索引作为Key(推荐)

给每个学生分配唯一ID,彻底避免索引作为Key带来的渲染问题:

// 初始化状态为包含唯一ID的对象数组
const [students, setStudents] = useState([{ id: Date.now(), content: "" }]);

// 映射组件时使用学生ID作为Key
{students.map((student) => (
  <Student 
    setStudents={setStudents} 
    students={students} 
    id={student.id} 
    key={student.id} 
    content={student.content} 
  />
))}

// 修改AddStudent组件,添加带唯一ID的学生对象
function AddStudent(props) {
    const {setStudents} = props;
    return (
        <button className="change-student add-student" onClick={() => {
            setStudents((prev) => [...prev, { id: Date.now(), content: "" }]);
        }}>
            +
        </button>
    );
}

// 修改RemoveStudent组件,按学生ID删除
function RemoveStudent(props) {
    const {setStudents, id} = props;
    return (
        <button className="change-student remove-student" onClick={() => {
            setStudents(prevStudents => prevStudents.filter(student => student.id !== id));
        }}>
            -
        </button>
    )
}

核心原理

当React组件的key发生变化时,会销毁旧组件实例并创建新的组件实例,此时Input的defaultValue会重新读取最新的状态值,从而实现内容更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 09:45:25