【问题标题】:Avoid re-rendering a big list of items with react-redux避免使用 react-redux 重新渲染大量项目
【发布时间】:2016-12-06 17:45:52
【问题描述】:

我正在为我的应用程序使用带有 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 的项目,那么只有这个项目将被重新渲染?

【问题讨论】:

标签: reactjs redux react-redux


【解决方案1】:

在渲染大列表时,您需要考虑几件事:

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

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

这将起作用,如果您在 <Item/> 组件类中放入 console.log('item rendered'),您会注意到只有一个调用。

但是(这是一个很大的但是),在使用 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 以执行列表的虚拟化(即仅显示用户实际可以看到的项目)。

【讨论】:

  • >当使用 react-redux 时,如果状态的任何部分发生变化,所有依赖于它们 ownProps 的连接组件将始终重新呈现。为什么当状态改变时包装组件会重新渲染?你的意思是当 ownProp 改变的时候?包装组件如何重新渲染而不是包装组件?包装组件不调用被包装组件的render()吗?它在文档、react-redux 代码或其他地方是这样说的吗?
【解决方案2】:

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

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

[...] react-redux 负责这个。它允许您指定您关心的状态的特定部分,并在相关部分未更改时小心避免更新 React 组件。

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

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

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

【讨论】:

  • 您几乎 100% 正确,只有一个使用 ownProps 的问题可以让您的所有包装器组件每次都重新渲染。看看我的回答!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-21
  • 2019-04-27
  • 2020-12-21
  • 1970-01-01
  • 2018-04-18
相关资源
最近更新 更多