React折叠表格子组件状态保留:父组件状态变更时保持行展开
解决React折叠表格行展开状态被重置的问题
方案一:将展开状态提升到父组件管理
把每行的展开状态从Row组件内部移到父组件的state中,这样父组件更新products时,展开状态不会丢失。
修改后的代码示例:
export default function Products() { const [products, setProducts] = useState([]); // 用对象存储每个产品的展开状态,key为产品唯一标识(比如id) const [expandedRows, setExpandedRows] = useState({}); // 切换行展开状态的方法 const toggleRow = (productId) => { setExpandedRows(prev => ({ ...prev, [productId]: !prev[productId] })); }; function Row({ product, isOpen, onToggle }) { return ( <tr> <td onClick={() => onToggle(product.id)}>{product.name}</td> <td> {isOpen && ( <button onClick={(e) => { e.stopPropagation(); // 阻止点击按钮触发行折叠 // 编辑逻辑:更新产品数据 const updatedProducts = products.map(p => p.id === product.id ? {...p, name: '更新后的名称'} : p ); setProducts(updatedProducts); }}>编辑</button> )} </td> {/* 折叠内容区域 */} {isOpen && <tr><td colSpan="2">产品详情:{product.description}</td></tr>} </tr> ); } return ( <table> <tbody> {products.map(product => ( <Row key={product.id} product={product} isOpen={expandedRows[product.id] || false} onToggle={toggleRow} /> ))} </tbody> </table> ); }
核心要点:
- 父组件通过
expandedRows对象统一管理所有行的展开状态,依赖产品唯一标识(如id)做key - 点击编辑按钮时必须调用
e.stopPropagation(),避免触发行的折叠事件 - 给每个Row组件设置唯一
key(不能用数组索引),确保React正确识别组件实例
方案二:将Row组件提取到父组件外部并保留内部状态
如果不想把状态提升到父组件,可以把Row组件移到Products组件外部,配合唯一key让React复用组件实例,从而保留内部的展开状态。
代码示例:
// 将Row组件提取到Products外部 function Row({ product, onUpdateProduct }) { const [openRow, setOpenRow] = useState(false); return ( <tr> <td onClick={() => setOpenRow(!openRow)}>{product.name}</td> <td> {openRow && ( <button onClick={(e) => { e.stopPropagation(); // 通过回调让父组件处理产品更新 onUpdateProduct(product.id, { name: '更新后的名称' }); }}>编辑</button> )} </td> {openRow && <tr><td colSpan="2">产品详情:{product.description}</td></tr>} </tr> ); } export default function Products() { const [products, setProducts] = useState([]); const updateProduct = (productId, newData) => { const updatedProducts = products.map(p => p.id === productId ? {...p, ...newData} : p ); setProducts(updatedProducts); }; return ( <table> <tbody> {products.map(product => ( <Row key={product.id} // 必须设置唯一key product={product} onUpdateProduct={updateProduct} /> ))} </tbody> </table> ); }
核心要点:
- Row组件必须定义在Products外部,避免父组件重渲染时创建新的组件类型,导致旧组件被卸载、状态丢失
- 给每个Row设置唯一
key,确保React能复用组件实例,仅更新props而非重新挂载 - 产品更新逻辑通过回调函数传递给父组件,父组件更新products时,Row组件仅做props更新,内部状态不受影响
内容的提问来源于stack exchange,提问作者SanBarra
相关产品推荐
相关产品推荐

