React – PureRenderMixin Performance (Simple Overview)

このエントリーをはてなブックマークに追加

PureRenderMixin

React has a Mixin called PureRenderMixin which is included in the addon in the current version(0.12.2). The specification is as below.

If your React component’s render function is “pure” (in other words, it renders the same result given the same props and state), you can use this mixin for a performance boost in some cases.

Overview

Without PureRenderMixin

Let’s create a simple example without the PureRenderMixin. This changes (or just sets the same value in) the value state when the button is clicked.

When clicking the ‘Count Up Event’ button, the value increments by 1 and ‘render’ is called as well. In addition, when clicking the ‘Set Same Value Event’ button, the render is being called despite the value not changing. This is meaningless and inefficient.

With PureRenderMixin

So what happens if we add the PureRenderMixin? Let’s look at the example below.

You can see the difference when clicking the ‘Set Same Value Event’ button. The render isn’t called this time.

And?

What actually happened in the example above? Let’s dive into the code and see the implementation of PureRenderMixin.(Open Source is awesome :))

var ReactComponentWithPureRenderMixin = {
  shouldComponentUpdate: function(nextProps, nextState) {
    return !shallowEqual(this.props, nextProps) ||
           !shallowEqual(this.state, nextState);
  }
};

You can see that PureRenderMixin is implementing the ‘shouldComponentUpdate’ and inside it, it compares the props and state using shallowEqual. The implementation of shallowEqual is going to be extremely important to remember. Let’s look at it.

function shallowEqual(objA, objB) {
  if (objA === objB) {
    return true;
  }
  var key;
  // Test for A's keys different from B.
  for (key in objA) {
    if (objA.hasOwnProperty(key) &&
        (!objB.hasOwnProperty(key) || objA[key] !== objB[key])) {
      return false;
    }
  }
  // Test for B's keys missing from A.
  for (key in objB) {
    if (objB.hasOwnProperty(key) && !objA.hasOwnProperty(key)) {
      return false;
    }
  }
  return true;
}

Summary of shallowEqual
* If A and B’s value or reference is same, it returns true
* Iff A’s property isn’t included in B, it returns false
* If A’s property is in B and the value or reference differs, it returns false
* If B’s property isn’t included in A’s, it return false
* If none of the statements above match, it returns true

Let’s return to the button example above and look at the handleNoChangeClick. You can see that it is calling setState but is setting the same value as it had. (it’s not changing anything)

handleNoChangeClick : function() {
  // intentionally set the same value
  this.setState({value : this.state.value});
}

Hence, it is considered to be the same value and shouldComponentUpdate will be false which means it will not render.

CAUTION

PureRenderMixin seems nice and will save some typing for us to write in the shouldComponentUpdate, but we ought to know the specifications clearly before we use it. Which is also written as a caution in the docs as follow.

This only shallowly compares the objects. If these contain complex data structures, it may produce false-negatives for deeper differences. Only mix into components which have simple props and state, or use forceUpdate() when you know deep data structures have changed.

 What this means could be explained in an example below.

This isn’t using PureRenderMixin and setState triggers render every time, which changes the value as intended despite the bad practice. Now let’s include the PureRenderMixin in the example below.

You can see that the ‘increment’ button changes nothing. This is because of the shallowEqual specifications. It only compares the value or the reference and the array values hasn’t changed in this situation. Clicking the ‘Force Render’ button will trigger the render and you can see the button clicked was actually incrementing the values.

A Simple Performance Overview

Without PureRenderMixin

The Code

We’re going to continue using the examples above but we’re going to change it in order to use child components. 1 row will be 1 child component and every child component will manage how many times they have rendered.

Results

You can see that clicking ‘increment’ triggers all the component’s render and it’s pretty slow. (about 1.2 secs)

スクリーンショット 2015-03-08 14.40.42

スクリーンショット 2015-03-08 14.41.26

With PureRenderMixin

The Code

 Now let’s add the PureRenderMixin.

 This time you can see that clicking ‘increment’ only triggers the parent component and the target child component’s render. In addition, it only takes about 0.12 secs and it’s pretty much responsive.

スクリーンショット 2015-03-08 14.41.58

スクリーンショット 2015-03-08 14.41.31

I’m not sure if the parent-child means of updating the state is following the FLUX way, but the examples above do show how PureRenderMixin will help you boost the performance (as the docs say). Seems very useful.

Written on March 8, 2015
このエントリーをはてなブックマークに追加