`why-did-you-update` is your new best friend
Try as you might to make your components as performant as possible — and no, that doesn't mean making all your components PureComponent
s — it's quite possible you missed a few spots. Why not accept a helping hand from your new friend, why-did-you-update
?
why-did-you-update
is a small library that you wrap over React while you're developing that lets you know when a component has re-rendered uselessly.
Setting it up
You install it with NPM:
npm install --save why-did-you-update
...or Yarn, if you prefer:
yarn add why-did-you-update
Then, in your code, you call it on your React library:
import React from 'react';
const { whyDidYouUpdate } = require('why-did-you-update');
whyDidYouUpdate(React);
But be careful! You don't want to ship this into production, so you'll need to tell you're in development mode one way or another, and only enable it as necessary:
import React from 'react';
if (process.env.NODE_ENV !== 'production') {
const { whyDidYouUpdate } = require('why-did-you-update');
whyDidYouUpdate(React);
}
With these few lines of code, your React is all wired up for why-did-you-update
to let you know in your browser's console whenever a component can potentially be optimized.
Reading the output
- Do something in your app to trigger a change;
- Observe a river of components flowing in your browser's console;
- Go through the five stages of grief in quick succession.
Confronting your less than ideal job of ensuring your components don't re-render all over the place can feel both terrifying and liberating, but mostly the latter: it ultimately gives you a chance to squeeze out more performance out of your app! (That's what I always tell myself.)
Here are some of the things why-did-you-update
lets you know about about your components that have re-rendered
Even though none of their props or state changed
Value is the same (equal by reference). Avoidable re-render!
When props
or state
as a whole stays the same, meaning the new reference points to the same object as teh old one, but the component still re-renders, consider using a PureComponent
if appropriate.
When their props / state changed, but not really
Value did not change. Avoidable re-render!
Something in the props
or state
has changed its reference, although on a deep rather than shallow compare, it turns out it still contains the same data.
Numbers, strings and the such will always be equal themselves:
1 === 1; // true
'foo' === 'foo'; // true
null === null; // true
undefined === undefined; // true
Others may have the "same" value but a different reference, in particular objects and arrays:
[] === []; // false
{ name: 'Dan' } === { name: 'Dan' }; // false
In the case of objects, see whether it makes sense to spread out the object to individual props rather than passing it as a single prop.
The library helpfully points out which of the properties are deep-equal but now shallow-equal.
When the only thing that changed are functions
Changes are in functions only. Possibly avoidable re-render?
Functions are another thing that may trip up our performance enhancements. Since there are legitimate reasons for passing a new function to a component, the suggestion is phrased with a question mark. But most of the time, new functions are accidental and can be fixed.
First off, make sure you're not bind
-ing a function in place:
<Component onClick={this.handleClick.bind(this)} />
...and in the more general sense look for ways to cache the reference to that function, if it does not fundamentally change between renders.
What it can't tell you
While why-did-you-update
already offers you a lot to work with, it has some caveats:
- It can't tell that functional components (components defined as plain functions) have updated uselessly (issue). But functional components update uselessly most of the time, so consider avoiding them if you're concerned about their performance.
- It doesn't work with components that have a custom
componentDidUpdate
method (issue);
Changes in props or state that don't have an effect on the DOM output
Another thing that the library can't identify yet is when the component re-renders due to a change in the props
or state
, but the component's DOM stays the same.
It might be a sign you're putting things in props
or state
that you actually don't need for rendering the component. If that's the case, you can prune them out or store them in a different place.