PureComponent的作用及一些使用陷阱

默认渲染行为的问题

在React Component的生命周期中,有一个shouldComponentUpdate方法。这个方法默认返回值是true。

这意味着就算没有改变组件的props或者state,也会导致组件的重绘。这就经常导致组件因为不相关数据的改变导致重绘,这极大的降低了React的渲染效率。比如下面的例子中,任何options的变化,甚至是其他数据的变化都可能导致所有cell的重绘。

//Table Component
{this.props.items.map(i =>
    <Cell data={i} option={this.props.options[i]} />
)}

重写shouldComponentUpdate

为了避免这个问题,我们可以在Cell中重写shouldComponentUpdate方法,只在option发生改变时进行重绘。

class Cell extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.option === nextProps.option) {
      return false;
    } else {
      return true;
    }
  }
}

这样每个Cell只有在关联option发生变化时进行重绘。

使用PureComponent与immutable.js

因为上面的情况十分通用,React创建了PureComponent组件创建了默认的shouldComponentUpdate行为。这个默认的shouldComponentUpdate行为会一一比较props和state中所有的属性,只有当其中任意一项发生改变是,才会进行重绘。

需要注意的是,PureComponent使用浅比较判断组件是否需要重绘

因此,下面对数据的修改并不会导致重绘(假设Table也是PureComponent)

  options.push(new Option())
  options.splice(0, 1)
  options[i].name = "Hello"

这些例子都是在原对象上进行修改,由于浅比较是比较指针的异同,所以会认为不需要进行重绘。

为了避免出现这些问题,推荐使用immutable.js。immutable.js会在每次对原对象进行添加,删除,修改使返回新的对象实例。任何对数据的修改都会导致数据指针的变化。

其他的陷阱

需要注意的是,还有一些小陷阱需要避免。

  1. Literal Array与Literal Object
{this.props.items.map(i =>
    <Cell data={i} options={this.props.options || []} />
)}

若options为空,则会使用[]。[]每次会生成新的Array,因此导致Cell每次的props都不一样,导致需要重绘。解决方法如下:

const default = [];
{this.props.items.map(i =>
  <Cell data={i} options={this.props.options || default} />
)}
  1. 内联函数
    函数也经常作为props传递,由于每次需要为内联函数创建一个新的实例,所以每次function都会指向不同的内存地址。比如:
render() {
     <MyInput onChange={e => this.props.update(e.target.value)} />;
}

以及:

update(e) {
     this.props.update(e.target.value);
}
render() {
     return <MyInput onChange={this.update.bind(this)} />;
}

注意第二个例子也会导致创建新的函数实例。为了解决这个问题,需要提前绑定this指针:

constructor(props) {
    super(props);
    this.update = this.update.bind(this);
  }
  update(e) {
    this.props.update(e.target.value);
  }
  render() {
    return <MyInput onChange={this.update} />;
  }

参考资料

  1. Optimizing Performance - React
  2. react.js pure render performance anti pattern

推荐阅读更多精彩内容

  • JSX 知识准备 JSX 并不是一门全新的语言,仅仅是一个语法糖,允许开发者在javascript中编写XML语言...
    艾伦先生阅读 4,081评论 4 20
  • 按照惯例,先给ReactJS背书 React是一个Facebook开发的UI库,于2013年5月开源,并迅速的从最...
    艾伦先生阅读 3,049评论 1 12
  • 自己最近的项目是基于react的,于是读了一遍react的文档,做了一些记录(除了REFERENCE部分还没开始读...
    潘逸飞阅读 2,814评论 1 10
  • 公司打算用react-native开发APP,初始RN遇到了很多坑,搭建了一个小的项目框架,结合redux根据公司...
    45b645c5912e阅读 608评论 0 5
  • “耶!我赢啦!”毛毛扔下游戏手柄欢呼雀跃着! “爸,我们中午吃什么?” “披萨、汉堡、意面······随便你挑。”...
    慕子笙阅读 186评论 0 1
  • 1.孩子本来是有专注力的,但后来变得不专注是因为大人经常地打断他们 2.保护孩子的灵活性,把我的条条框框藏起来 孩...
    Amy心灵之屋阅读 345评论 0 0
  • 奴隶社会文章摘抄: 永远没有完全准备好的那一天,只有尝试了才知道如何改进,离梦想更进一步。 很多时候,我们有各种想...
    红虾小姐阅读 143评论 0 0