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

React列表项删除异常:删除目标项时连带删除后续项

React列表删除异常问题

我正在做一个React基础学习小应用,功能是输入内容后在下方生成列表项。但点击列表项的删除按钮时,本该只删当前项,结果连后面所有项都一起删了。排查时发现,deleteHandler函数内部的item状态数组只有待删除项之前的内容,但函数外部item能正常显示所有项,这是为什么?


相关代码

App.js

const App = () => {
    const [item, setItem] = useState([]);
    console.log(item);
    for (const i of item){
        console.log(i.props.id);
    }
    const [itemCount, setItemCount] = useState(0);
    const handleNewName = (newName) => {
        console.log("new name has been recieved: "+newName);
        setItem([...item, <ListItem itemName={newName} deleteHandler={deleteHandler} key={itemCount} id={itemCount} />]);
        setItemCount(currentCount => currentCount + 1);
        console.log(itemCount);
        
    }

    const deleteHandler = (key) => {
        setItem(item.filter(i=>i.props.id !== key));
    }
    return (
    <>
        <InputArea handleNewName={handleNewName}/>
        {item}
    </>
    );
}

export default App

inputArea.js

export default function InputArea(props){
  const [inputValue, setInputValue] = useState("");
  const handleNewName=props.handleNewName;
  let currName="";
  function snagName(){
    currName = inputValue;
    setInputValue("");
    handleNewName(currName);
  }
  return (
    <>
    <input value={inputValue} onChange={(e)=>setInputValue(e.target.value)}/>
    <button onClick={snagName}>Submit</button>
    <br/>
    </>
  );
}

listItem.js

export default function ListItem (props){
  let currName=props.itemName; 
  let id = props.id;
  const deleteHandler = props.deleteHandler;
  const clickHandler = () => deleteHandler(id);
  return (
    <>
    <button onClick={clickHandler}>Delete</button><span>Hello, {currName}</span><br/>
    </>
  )
}

问题原因

1. 闭包陷阱捕获旧状态

handleNewName和deleteHandler在组件首次渲染时创建,会捕获当时的item初始状态(空数组)。后续每次调用setItem时,你使用的...item始终是闭包中保存的旧状态,而非实时最新状态。比如添加多个项后,deleteHandler里的item还是早期的旧数组,filter操作只能保留旧数组中未被删除的项,新添加的项不在这个旧数组里,导致执行setItem后新项全部丢失,看起来就是删除当前项时连带删掉了后面的所有项。

2. 状态存储JSX元素的错误做法

React状态的职责是存储纯数据,而非直接存储JSX组件元素。你把<ListItem>存在item状态里,将渲染逻辑和数据绑定在一起,不仅让状态难以维护,还会放大闭包带来的状态不一致问题。


修复方案

核心修改点

  • 状态存储数据对象,而非JSX元素
  • 使用函数式更新获取最新状态,规避闭包陷阱

修改后的代码

App.js

import { useState } from 'react';
import InputArea from './inputArea';
import ListItem from './listItem';

const App = () => {
    // 改为存储数据数组,每个元素包含id和name
    const [items, setItems] = useState([]);
    const [itemCount, setItemCount] = useState(0);

    const handleNewName = (newName) => {
        // 函数式更新,prevItems是最新状态快照
        setItems(prevItems => [
            ...prevItems,
            { id: itemCount, name: newName }
        ]);
        setItemCount(currentCount => currentCount + 1);
    };

    const deleteHandler = (key) => {
        // 同样用函数式更新获取最新items
        setItems(prevItems => prevItems.filter(item => item.id !== key));
    };

    return (
        <>
            <InputArea handleNewName={handleNewName} />
            {/* 渲染时根据数据生成ListItem组件 */}
            {items.map(item => (
                <ListItem 
                    key={item.id} 
                    itemName={item.name} 
                    deleteHandler={deleteHandler} 
                    id={item.id} 
                />
            ))}
        </>
    );
};

export default App;

inputArea.js(优化空值判断)

import { useState } from 'react';

export default function InputArea(props){
  const [inputValue, setInputValue] = useState("");
  const handleNewName = props.handleNewName;

  function snagName(){
    // 避免输入空内容
    if (inputValue.trim()) {
      handleNewName(inputValue);
      setInputValue("");
    }
  }

  return (
    <>
      <input value={inputValue} onChange={(e)=>setInputValue(e.target.value)}/>
      <button onClick={snagName}>Submit</button>
      <br/>
    </>
  );
}

listItem.js(简化代码)

export default function ListItem (props){
  return (
    <>
      <button onClick={() => props.deleteHandler(props.id)}>Delete</button>
      <span>Hello, {props.itemName}</span>
      <br/>
    </>
  );
}

修复说明

  1. 状态存纯数据:现在items仅存储数据,渲染时再生成组件,彻底分离数据管理与渲染逻辑,符合React设计模式。
  2. 函数式更新:setItems(prevItems => ...)中的prevItems是React提供的最新状态快照,确保每次更新都基于当前最新数据,彻底解决闭包导致的旧状态捕获问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 15:40:01