React – PureRenderMixin Performance (Simple Overview)
Tweet
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)
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.
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.
