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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 13:52:49