在React组件开发中,列表渲染是非常常见的场景,而列表项的删除操作也是高频需求。很多开发者在实现删除功能时,会出现点击删除按钮后整个列表被清空的问题,无法做到只删除目标项。要解决这个问题,首先需要理解React中状态更新的逻辑和列表渲染的key机制。
常见的错误实现方式
很多新手会直接把列表状态设置为空数组来实现删除,这是导致一键清空的核心原因。我们来看一个错误的示例:
import { useState } from 'react';
function WrongList() {
const [list, setList] = useState(['苹果', '香蕉', '橙子', '葡萄']);
// 错误的删除函数,点击后会清空整个列表
const handleDelete = () => {
setList([]);
};
return (
<div>
<ul>
{list.map((item, index) => (
<li key={index}>
{item}
<button onClick={handleDelete}>删除</button>
</li>
))}
</ul>
</div>
);
}
export default WrongList;
上面的代码中,handleDelete函数直接调用setList([]),不管点击哪个列表项的删除按钮,都会把整个列表状态清空,这就是典型的一键清空问题。
正确实现精确删除的核心逻辑
要实现精确删除,核心是在删除函数中拿到目标列表项的唯一标识,然后从原列表中过滤掉该标识对应的项,而不是清空整个列表。通常我们会给列表项设置唯一的id作为key,而不是使用数组索引,避免索引变化导致的渲染问题。
步骤1:初始化带唯一id的列表数据
首先把列表数据改成对象数组,每个对象包含唯一的id和对应的内容:
const initList = [
{ id: 1, name: '苹果' },
{ id: 2, name: '香蕉' },
{ id: 3, name: '橙子' },
{ id: 4, name: '葡萄' }
];
步骤2:编写精确删除函数
删除函数需要接收目标项的id,使用数组的filter方法返回过滤后的新数组,只保留id不等于目标id的项:
const handleDelete = (targetId) => {
setList(prevList => prevList.filter(item => item.id !== targetId));
};
这里使用函数式的状态更新prevList => ...,可以保证拿到的是最新的列表状态,避免异步更新带来的问题。
步骤3:完整组件实现
把上面的逻辑整合到完整的React组件中:
import { useState } from 'react';
function CorrectList() {
const [list, setList] = useState([
{ id: 1, name: '苹果' },
{ id: 2, name: '香蕉' },
{ id: 3, name: '橙子' },
{ id: 4, name: '葡萄' }
]);
// 精确删除函数,接收目标项id
const handleDelete = (targetId) => {
setList(prevList => prevList.filter(item => item.id !== targetId));
};
return (
<div>
<h3>水果列表</h3>
<ul>
{list.map(item => (
<li key={item.id}>
{item.name}
<button onClick={() => handleDelete(item.id)}>删除</button>
</li>
))}
</ul>
<p>当前列表长度:{list.length}</p>
</div>
);
}
export default CorrectList;
注意事项
- 不要使用数组索引作为key,当列表有增删操作时,索引会变化,可能导致渲染异常。
- 删除函数一定要基于原列表做过滤,而不是直接设置为空数组,这是避免一键清空的核心。
- 如果列表数据是从接口获取的,要确保接口返回的每个项都有唯一且不变的id,不要用随机数作为id。
常见问题排查
如果还是出现删除异常,可以按照以下顺序排查:
- 检查删除函数是否接收到了正确的目标id,可以在函数内打印id确认。
- 检查列表的key是否使用的是唯一且稳定的值,不是数组索引。
- 检查状态更新是否使用了函数式更新,避免拿到旧的列表状态。