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

React点击删除按钮无法正确移除指定组件问题咨询

问题描述

这是我的Add_Blog.js文件:

import React, { useState } from "react";


function AddBlogs() {

const ADD = () => {
    return (
        <div>
            <label>Topic</label>
            <input></input>
            <label>Content</label>
            <textarea></textarea>
            <button type="button" onClick={deleteContent} value={state.length}> Delete</button>
        </div>
    );
}
const [state, setState] = useState([<ADD key="first"></ADD>]);


const addblog = () => {
    setState([...state, <ADD key={state.length}></ADD>])
}


const deleteContent = (event, key) => {
    setState([...state, state.splice(event.target.value, 1)]);

}

return (
    <div>
        <form>
            <div>
                <label>Add Title</label>
                <input></input>
            </div>
            <div>
                {
                    state
                }

            </div>
            <div>
                <button type="button" onClick={addblog}>Add another topic</button>
            </div>
            <input type="submit" value="publish"></input>
        </form>
    </div>
);
}
export default AddBlogs;

我对这段代码的预期效果是:点击删除按钮时,仅移除该按钮对应的单个输入组件。但实际运行时点击删除按钮,会移除目标组件以及其下方的所有组件,请问该问题应如何修复?

问题原因

代码里有两个核心bug,才会导致删除时多删组件:

  1. 删除逻辑完全写错了:Array.splice是直接修改原数组的方法,返回值是被删掉的元素组成的数组,不是删除后剩下的数组。你写的setState([...state, state.splice(event.target.value, 1)])相当于先把原数组从对应位置开始删元素,再把原数组剩余元素和刚删出来的元素重新拼接,元素顺序、数量全是错的。
  2. 删除按钮拿的索引不对:你给按钮设置的value={state.length}是渲染那一刻整个组件列表的总长度,根本不是当前输入块对应的索引。比如你加了3个输入块,所有删除按钮的value都是3,点任何一个都会从错误的位置开始删,而且新增组件的时候之前渲染好的按钮value不会自动更新,索引全是错位的。
    另外你直接把JSX元素存在state里的写法也不推荐,后续要拿输入值、做校验都会很麻烦,很容易出现状态不同步的问题;把单条输入的组件定义在父组件内部,每次重渲染都会重新创建组件引用,也会触发不必要的重渲染甚至状态异常。
修复方法

最稳妥的改法是把state从存JSX改成存纯数据,用唯一id标识每一个输入块,从根源避免索引错位的问题,修复后的完整代码如下:

import React, { useState } from "react";

// 单条主题输入组件抽离到父组件外,避免重复创建组件引用,接收当前项id和删除回调作为参数
const BlogItem = ({ id, onDelete }) => {
  return (
    <div>
      <label>Topic</label>
      <input />
      <label>Content</label>
      <textarea />
      {/* 点击时直接传入当前项的唯一id,不需要靠按钮value传索引 */}
      <button type="button" onClick={() => onDelete(id)}> Delete</button>
    </div>
  );
}

function AddBlogs() {
  // 自增id,保证每一个新增的输入块都有唯一标识,不使用索引作为标识
  const [nextId, setNextId] = useState(1);
  // state只存输入块的标识数据,后续要存输入内容直接扩展成对象即可
  const [blogList, setBlogList] = useState([{ id: 0 }]);

  const addBlog = () => {
    setBlogList([...blogList, { id: nextId }]);
    setNextId(nextId + 1);
  }

  const deleteBlog = (targetId) => {
    // 过滤掉对应id的项,不修改原数组,逻辑简单不会出错
    setBlogList(blogList.filter(item => item.id !== targetId));
  }

  return (
    <div>
      <form>
        <div>
          <label>Add Title</label>
          <input />
        </div>
        <div>
          {blogList.map(item => (
            <BlogItem 
              key={item.id} 
              id={item.id} 
              onDelete={deleteBlog}
            />
          ))}
        </div>
        <div>
          <button type="button" onClick={addBlog}>Add another topic</button>
        </div>
        <input type="submit" value="publish" />
      </form>
    </div>
  );
}
export default AddBlogs;

改完之后点击任意删除按钮,只会移除对应的那一个输入块,不会影响其他组件,后续要获取每个输入框的内容也只需要给state里的项加content、topic字段,绑定onChange即可。

如果你不想大改原有结构,只做最小修复的话,只需要改两处:

  1. 渲染ADD组件时传入当前索引,删除按钮直接用这个索引作为value,不要用state.length
  2. 把删除逻辑里的splice用法改掉,不要修改原数组,用filter过滤对应索引即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 02:54:21