避免使用react-redux重新渲染大量项目

时间:2016-08-01 15:11:15

标签: reactjs redux react-redux

我正在为我的应用程序使用带有react和typescript的redux。我正在使用我的应用程序的不同位置使用的许多项目。我的州看起来像这样:

{
    items: {42: {}, 53: {}, ... }, //A large dictionary of items
    itemPage1: {
        itemsId: [ 42, 34, 4 ],
        ...
    },
    itemPage2: { ... 
    },
    ...
}

用户可以修改items调度某些操作的某些属性。当发生这种情况时,我需要重绘每个页面中已修改的组件。问题是我的物品很大,我不能在每次小修改时重新绘制所有物品。我想知道这种方法是否有效:

  • 我有一个拳头组件<ItemPage1>,它连接到商店以获取存储在itemPage1下的树中的所有状态,例如商品列表ID:itemsId
  • <ItemPage1>内,我遍历itemsId属性以生成多个FilterItem组件:itemsId.map( itemId => return <FilterItem id=itemId>);
  • 最后,使用Item连接每个ownProps以获取状态的正确部分:

    const mapStateToItemProps = (state, ownProps) => {
        return {
            item: state.items[ownProps.id],
        }
    }
    const mapDispatchToItemProps = (dispatch, ownProps) => {
        return null;
    }
    const FilterItem = connect(
        mapStateToItemProps,
        mapDispatchToItemProps
    )(Item)
    

如果我更新了id 42的项目,你能否确认或反驳,那么只有这个项目会被重新渲染?

2 个答案:

答案 0 :(得分:10)

在渲染大型列表时,您需要考虑以下几点:

  • 降低需要渲染的DOM元素总数(通过不渲染屏幕上实际不可见的项目,也称为虚拟化)
  • 不要重新渲染未更改的项目

基本上,当用户编辑一行时,您要避免的是对列表(或页面)的完全重新呈现。这可以完全按照您的方式实现,即:通过仅将需要呈现的项目的ID传递给列表容器,并使用connect将这些ID映射到ownProps每个组件。如果您有转储<Item/>组件,则您的<ItemPage/>组件将创建已连接的connect(<Item/>)组件。

如果您在console.log('item rendered')组件类中放置<Item/>,您会注意到只有一个电话。

但是(并且它很大但是),使用react-redux时不明显的是依赖于ownProps的所有连接组件将<如果状态的任何部分发生变化,则强烈>始终重新呈现。在您的情况下,即使<Item/>组件不会重新渲染,它们的包装组件connect(Item)也会!如果您有几十个项目,如果需要快速调度操作(例如输入输入时),可能会遇到一些延迟。怎么避免呢?使用工厂函数将ownProps用作初始道具:

const mapStateToItemProps = (_, initialProps) => (state) => {
    return {
        item: state.items[initialProps.id],  // we're not relying on the second parameters "ownProps" here, so the wrapper component will not rerender
    }
}
const mapDispatchToItemProps = (dispatch, ownProps) => {
    return null;
}
const FilterItem = connect(
    mapStateToItemProps,
    mapDispatchToItemProps
)(Item)

我建议你看看this other answer

您可能也对这些出色的幻灯片感兴趣:Big List High Performance React & Redux

最后,您应该明确地查看react-virtualized来执行列表的虚拟化(即,仅显示用户可以实际看到的项目)。

答案 1 :(得分:0)

好的,我发现了这个讨论:https://github.com/reactjs/redux/issues/1303

在底部清楚地说明了(来自多个主角):

[...] react-redux会解决这个问题。它允许您指定您关注的状态的特定部分,并在相关部分未更改时小心不用更新React组件。

[...]只是想完全理解这里发生了什么,所以如果Redux商店得到更新但是一个特定的组件状态没有改变,Redux将不会触发forceUpdate( )该组件的方法? [...]

React-Redux的connect()函数生成的包装器组件会进行多项检查,以尽量减少实际组件重新呈现的次数。这包括shouldComponentUpdate的默认实现,并对进入组件的props执行浅等式检查(包括从mapStateToProps返回的内容)。所以是的,作为一般规则,连接组件只会在从状态中提取的值发生变化时重新呈现。

所以我相信我的实现很好,它不会重新渲染所有项目,因为只有一个项目会看到它的属性被修改。