Redux 不应该阻止重新渲染吗? [英] Shouldn't Redux prevent re-rendering?

查看:50
本文介绍了Redux 不应该阻止重新渲染吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

我知道我可以使用 shouldComponentUpdate() 来防止新的渲染,但我认为 Redux 会在内部做到这一点.

List.js

import React, { PropTypes, Component } from 'react';从'react-redux'导入{连接};从'./Item'导入项目;类列表扩展组件{使成为() {const { 项目 } = this.props;返回 (<div><h2>列表</h2>{items.map((item, index) => <Item key={index} name={item.name}/>)}

);}}const mapStateToProps = state =>({项目:state.items});导出默认连接(映射状态到道具)(列表);

Item.js

import React, { PropTypes, Component } from 'react';类项目扩展组件{使成为() {console.log('Render', this.props.name);//即使 props 没有改变,组件也会重新渲染返回 (<div>{this.props.name}

);}}Item.propTypes = {名称:PropTypes.string};导出默认项目;

解决方案

一点点现有技术(正如 Dan Abramov 喜欢说的):Redux 是一种状态管理工具.它提供了一个 HOC(connect),但 HOC 不负责组件管理.Redux 不会以任何方式管理组件的生命周期:它提供了一种有效存储和查询应用程序所需数据的方法.它很大程度上受到 Om 的影响,Om 是连接 React 的 Clojurescript 桥梁.事实上,redux 中的 store 非常类似于 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);//真的

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

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

我们的两个对象并不相同,但是 shallowCompare 返回 true 因为它只比较其参数的 keys.如果这对你来说足够好,你可以简单地扩展 React.PureComponent,它为你实现了 shouldComponentUpdate,并使用 shallowCompare 来计算 shallowCompare 的相等性code>props 和 state.

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

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

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

I have a List component that displays multiple Item components. List gets its data from the Redux store.

When the store is updated (because I deleted an item for example), all Items are re-rerendered. Why is that?

I know I could use shouldComponentUpdate() to prevent a new rendering, but I thought Redux would do it internally.

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;

解决方案

A little bit of prior art (as Dan Abramov likes to say): Redux is a state management tool. It provides a HOC (connect), but that HOC is not responsible for component management. Redux does not manage your component's lifecycle in anyway: it provides a means to efficiently store and query data your app needs. It's largely influenced by the Om, a Clojurescript bridge to React. In fact, stores in redux very much resemble the atom data type in Clojure.

Now, getting into the meat of your problem -- even if your data is exactly the same, and even if you did use shouldComponentUpdate, your component is still going to re-render. The reason is that Array.prototype.map always produces a new object on the heap. As a result they're not referentially equal. Some code to demonstrate this concept:

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

But if we use shallowEqual, we get a different result:

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

Why is that? That's because shallowCompare checks for value equality, comparing each value in the arrays. However, shallowEquals contains a potential pitfall for the uninitiated:

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

Our two objects aren't the same, but shallowCompare returns true because it only compares the keys of its arguments. If that's good enough for you, you can simply extend React.PureComponent, which implements shouldComponentUpdate for you, and uses shallowCompare to compute equality of props and state.

Enter Immutable.js. This does away with the need for shallowCompare entirely. Consider the following:

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

Internally, Immutable shares data, and can very efficiently compare data structures for deep equality. That being said, Immutable comes with trade-offs: data structures become much more opaque and can be more difficult to debug. All in all, I hope this answers your question. JSBin here: https://jsbin.com/suhujalovi/edit?html,js,console

这篇关于Redux 不应该阻止重新渲染吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆