React自定义组件列表增删异常:删单个组件导致全部被删求解决
React自定义组件列表删除异常问题解决
问题场景
要实现自定义组件列表的增删功能,添加功能正常,但删除单个组件时,其余组件也会被一并删除。测试发现普通对象数组用现有逻辑能正常增删,但换成React元素数组就出问题。
问题根源
- 直接存储React元素在state:把
<Item>组件实例存在itemList里,React无法精准追踪每个组件的身份,删除操作后易触发错误重渲染。 - 闭包导致索引失效:添加组件时传递给
onDeleteProduct的index是创建时的固定值,删除后数组索引变化,后续删除会用旧索引过滤,导致错误删除多个项。 - Key值依赖动态索引:Item组件的key用基于数组长度生成的
number,删除后后续组件的number与实际位置不匹配,React误判组件身份,导致批量卸载。 - Ref数组维护混乱:手动用数组维护组件ref,删除后ref数组未同步更新,和实际组件列表脱节。
解决方案
核心思路是在state中存储数据而非React元素,让React基于数据渲染组件,同时用唯一标识追踪每个项:
修正后的完整代码
import React, { useState, useRef, useCallback, useImperativeHandle } from 'react'; export default function List() { const [itemList, setItemList] = useState([]); const itemListRef = useRef({}); // 用对象存储ref,键为item的唯一ID // 基于ID删除,彻底避免索引问题 const onDeleteProduct = useCallback(id => { setItemList(prevList => prevList.filter(item => item.id !== id)); // 同步删除对应ref delete itemListRef.current[id]; }, []); function addSelectProduct() { // 生成唯一ID,实际项目可使用uuid库 const newItem = { id: Date.now(), data: '' }; setItemList(prevState => [...prevState, newItem]); } function allData() { // 遍历所有组件ref获取数据 Object.values(itemListRef.current).forEach(ref => { ref && console.log(ref.getData()); }); } return ( <> <button onClick={addSelectProduct}>Add One</button> <button onClick={allData}>show All data</button> <table> <thead> <tr> <th># No. </th> <th>text </th> <th>Operate</th> </tr> </thead> <tbody> {/* 基于数据渲染组件,用唯一ID作为key */} {itemList.map((item, index) => ( <Item key={item.id} ref={r => itemListRef.current[item.id] = r} number={index + 1} id={item.id} onDeleteProduct={onDeleteProduct} /> ))} </tbody> </table> </> ); } const Item = React.forwardRef(({ number, id, onDeleteProduct }, ref) => { const [data, setData] = useState(""); useImperativeHandle(ref, () => ({ getData: () => data })); return ( <tr> <td>{number}</td> <td><input value={data} onChange={e => setData(e.target.value)}/></td> <td> <button size="sm" variant="danger" onClick={() => onDeleteProduct(id)}>Delete</button> </td> </tr> ); });
关键修正说明
- State存储数据对象:
itemList存储每个项的id和data,让React基于数据渲染组件,更新逻辑更可靠。 - 唯一ID作为组件Key:用
item.id作为key,React能精准识别每个组件,删除后不会错误卸载其他组件。 - 基于ID执行删除:删除时通过
id过滤数组,彻底避免索引变化和闭包带来的错误。 - Ref用对象管理:
itemListRef.current是对象,以item的id为键存储ref,增删时同步维护,避免数组索引混乱。
内容的提问来源于stack exchange,提问作者Trouble Lee
相关产品推荐
相关产品推荐

