【问题标题】:Shouldn't Redux prevent re-rendering?Redux 不应该阻止重新渲染吗?
【发布时间】:2017-03-03 13:00:35
【问题描述】:

我有一个 List 组件,它显示多个 Item 组件。 List 从 Redux 存储中获取数据。

当商店更新时(例如因为我删除了一个项目),所有Items 都会重新渲染。 为什么会这样?

我知道我可以使用 shouldComponentUpdate() 来阻止新的渲染,但我认为 Redux 会在内部完成。

List.js

import React, { PropTypes, Component } from 'react';
import { connect } from 'react-redux';

import Item from './Item';

class List extends Component {
  render() {
    const { items } = this.props;

    return (
      <div>
        <h2>List</h2>
        {items.map((item, index) => <Item key={index} name={item.name} />)}
      </div>
    );
  }
}

const mapStateToProps = state => ({
  items: state.items
});

export default connect(
  mapStateToProps
)(List);

Item.js

import React, { PropTypes, Component } from 'react';

class Item extends Component {
  render() {
    console.log('Render', this.props.name); // The component is re-rendered even if the props didn't change
    return (
      <div>
        {this.props.name}
      </div>
    );
  }
}

Item.propTypes = {
  name: PropTypes.string
};

export default Item;

【问题讨论】:

  • 因为你在 props 中有一个新的数组项。
  • @Andrew 我知道List 被重新渲染,因为items 属性已更新。但是Item 呢?它的道具没有改变

标签: reactjs redux react-redux


【解决方案1】:

一点现有技术(正如 Dan Abramov 喜欢说的):Redux 是一个状态管理工具。它提供了一个 HOC (connect),但该 HOC不负责组件管理。 Redux 无论如何都不会管理组件的生命周期:它提供了一种有效存储和查询应用程序所需数据的方法。它在很大程度上受到 Om 的影响,Om 是一个连接到 React 的 Clojurescript 桥梁。事实上,redux 中的 stores 非常类似于 Clojure 中的 atom 数据类型。

现在,进入您的问题的核心——即使您的数据完全相同,即使您确实使用了shouldComponentUpdate,您的组件仍然会重新渲染。原因是Array.prototype.map 总是 在堆上生成一个新对象。因此,它们在引用上不相等。一些代码来演示这个概念:

const myArr = [1, 2, 3]
const myArrCopy = myArr.map((n) => n);
myArr === myArrCopy // false

但如果我们使用shallowEqual,我们会得到不同的结果:

const myArr = [1, 2, 3]
const myArrCopy = myArr.map((n) => n);
React.addons.shallowCompare(myArr, myArrCopy); // true

这是为什么呢?这是因为shallowCompare 检查值相等,比较数组中的每个值。然而,shallowEquals 包含一个潜在的陷阱:

const myObject = { foo: 'bar' };
const myObject2 = { foo: 'baar' };
React.addons.shallowCompare(myObject, myObject2); // true

我们的两个对象不一样,但shallowCompare 返回true,因为它只比较其参数的。如果这对您来说足够好,您可以简单地扩展React.PureComponent,它为您实现shouldComponentUpdate,并使用shallowCompare 来计算propsstate 的相等性。

输入Immutable.js。这完全消除了对shallowCompare 的需求。考虑以下几点:

const myList = Immutable.List([1, 2, 3]);
const myListCopy = myList.map((n) => n);
myList.equals(myListCopy) // true

在内部,Immutable 共享数据,并且可以非常有效地比较数据结构以实现深度相等。话虽如此,不可变也带来了权衡:数据结构变得更加不透明并且更难以调试。总而言之,我希望这能回答你的问题。 JSBin 这里:https://jsbin.com/suhujalovi/edit?html,js,console

【讨论】:

  • 非常感谢您的回答
  • Reducer 在 Redux 中是纯函数这一事实怎么办?它是否比 Flux 带来任何性能优势? (我认为它可以防止不必要的渲染)
  • 您可以将 any 函数设为纯函数。只要函数的“依赖项”是专有它的参数。 Redux 减少了 Flux 附带的大量样板;这就是为什么它现在是生产 React 应用程序的事实标准。纯函数的纯事实并不能保证效率。所以,tldr,Redux 并没有带来开箱即用的性能优势。管理渲染/生命周期仍然是您的责任。
  • 不,不是。软件工程中没有灵丹妙药。但话虽如此,Redux 让解决性能问题变得非常容易,因为如果你的 reducer 是纯的,那么实现 shouldComponentUpdate 会容易得多。
  • @natnai 的好回答。如果您想了解更多信息,我在这篇博文中描述了 Redux 性能的一些关键问题:blog.isquaredsoftware.com/2017/01/…
【解决方案2】:
{items.map((item, index) => <Item key={index} name={item.name} />)}

这一行总是在父组件中创建一个新数组,每次都生成“新”项目组件

【讨论】:

  • 那么解决方案是什么?我应该直接将Item 连接到商店吗?
  • 我更喜欢“shouldComponentUpdate”方法:)
  • 该死的。我认为使用 Redux(及其非变异 reducer)而不是 Flux 的主要好处之一是避免这个问题:/
  • 我1年前用过flux,现在用redux,对我来说,主要的好处是“组织”和“缩放”,当你看不到主状态应用程序时,flux开始变得一团糟集中在一个地方,而不是分散在许多商店中
  • 我同意这一点,但我认为也会有性能优势。
猜你喜欢
  • 2020-07-06
  • 1970-01-01
  • 2021-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-22
  • 2015-08-15
  • 2023-03-28
相关资源
最近更新 更多