Should I use shouldComponentUpdate?

So you heard React was fast, and decided to give it a go. You grabbed a boilerplate, started working through it, and noticed shouldComponentUpdate and PureRenderMixin. Some googling reveals these are the things you use to make React fast. But wasn’t React already fast?

The answer is that React is fast… sometimes. The other times, you should use shouldComponentUpdate. But when are the other times? Wouldn’t it be so much easier if there was just a simple yes-no question which you could ask to decide whether to use shouldComponentUpdate or not? And wouldn’t you believe it, there is!

Will shouldComponentUpdate provide a measurable and perceivable performance improvement?

If not, don’t use it.

So you mean I probably shouldn’t be using it?

You probably should avoid it. The React team called shouldComponentUpdate a performance escape hatch for a reason — and you’d better bloody well hope that an escape hatch isn’t meant for regular use. Of course, “probably not” doesn’t mean “definitely not”.

React wouldn’t have a performance escape hatch if it wasn’t sometimes useful. The trick is that you need to ask yourself if you should use it — not whether you shouldn’t. And if you’re not a special snowflake, well, you’ll probably use it less often than not.

But hang on a moment, you say. My circumstances are different. And… you’re probably right. So let’s have a look at the reasoning behind the rule, starting with:

Adding shouldComponentUpdate will often slow your component down

Yes, you read that correctly. A performance escape hatch can actually slow your component down. But how? To answer this question, we need to zoom out for a moment to a sky-high level, and ask: what is React?

React is basically a very clever implementation of shouldComponentUpdate.

But James, you say. React does a lot more than just figure out whether to update a component. And you’re right. As well as deciding if a component should update, React also decides how to do it. But you can’t have one without the other, and it turns out React is generally pretty good at both.

Ok, so React is good at deciding whether a component should update. How does it work this magic?

Great question! So you know those render methods which you write for each of your components? It may look like they’re returning funny HTML-in-JavaScript (or ReactElement objects if you use Raw React). But you may be surprised to hear that render actually returns plain old JavaScript objects. They look something like this:

{
    type: 'ul',
    props: { className: 'what-do-you-want-to-do-tonight' },
    children: [
        { type: 'li', children: 'The same thing we do every night, pinky.' },
    ]
}

Sure, it might be all dressed up in JSX or calls to React.createElement, but the true form of render()‘s output is just an object representing your markup. And if that object hasn’t changed from the previous render, there is obviously no need to update the DOM.

Or to put it another way, React basically implements shouldComponentUpdate for you for free. If we pretend render receives its props as an argument instead of attached to this, React’s implementation would look something like this:

shouldComponentUpdate(nextProps) {
    return !deepEquals(render(this.props), render(nextProps))
}

As you know, deepEquals will be fast on small objects, and slow on heavily nested ones. So this approximation gives us a good rule of thumb:

If the value returned by render is tiny but props is heavy, shouldComponentUpdate will likely do more harm than good.

But say you have a case where the value returned by render() is large enough that you can gain something by writing shouldComponentUpdate yourself?

shouldComponentUpdate gains are often insignificant

So let’s say you’re rendering something heavy like a HTML table. You’re pulling your data from your props, but you’re also doing some computations on it within your render method. And to make this the perfect scenario, your data uses Immutable.js such that changes in props can be found with simple reference equality.

In this scenario, shouldComponentUpdate will provide a huge speed up over React’s default behaviour. In fact, my experience has been that a 10 times speedup is not unusual at all. But if this is the case, how can the gains be insignificant? The answer is of course:

A 10x speedup of less than 1 millisecond is still less than 1 millisecond.

Donald Knuth wrote that “premature optimization is the root of all evil” years before JavaScript was even imagined, but that does not make it any less relevant today.

Of course, us JavaScript programmers are not known for learning things the easy way, and a number of you are probably now asking “but some things are less evil than others, right?” After all, how much can a short three-line function really hurt when it isn’t even going to slow things down? Actually, my experience is that it can be downright painful.

Maintaining shouldComponentUpdate is hard

So if I had to guess, the reason the React team called shouldComponentUpdate an “escape hatch” instead of a “turbo button” is that maintenance is hard. But honestly, I think an “escape hatch” is the wrong analogy. shouldComponentUpdate is actually more like unprotected sex.

The thing is, shouldComponentUpdate is sometimes necessary, and certainly makes your app respond better. But it also is a major cause of bugs — and not ones which appear straight away.

But seriously, maintaining code with shouldComponentUpdate is hard. Add a new prop to render but forget to update shouldComponentUpdate? There’s a bug. Pass in a factory function as a prop which uses state from somewhere innocent like the current route? Another bug. And the worst thing is that these bugs often don’t appear in tests, because they don’t break until something, somewhere changes. And probably when you first demonstrate it to your customer at that.

But if shouldComponentUpdate truly causes this many bugs, it obviously must still be used on a somewhat regular basis. So the question becomes…

When should I use shouldComponentUpdate?

At the risk of repeating myself, this question has a very simple answer:

Use shouldComponentUpdate once you’ve measured that it provides a perceivable performance improvement.

Hopefully the previous section has convinced you that shouldComponentUpdate has a number of downsides. With these in mind, the trick is to only use it when you’re sure the upsides beat them. And to do that, you’ll need to use a ruler. A really fancy technological one.

Measuring performance improvements

While there are a number of ways you could try to measure the improvement given by shouldComponentUpdate, this article is going to focus on doing so with a JavaScript Profiler.

But what is a profiler? It’s that fancy ruler I mentioned. A good profiler will let you measure basically anything about your program. But in particular, it allows you to break down your script’s total running time by your functions:

Profiler

By comparing the time spent in render both before and after we add shouldComponentUpdate, we can put a number to any performance improvement. Of course, we’ll also need to take into account time spent in shouldComponentUpdate — it isn’t free. Finally, with some mental gymnastics we can decide if the extra maintenance and general uncouthness of shouldComponentUpdate is worth the speedup.

Easy as 1-2-3, right? But don’t take my word for it – let’s walk through an exercise which demonstrates it. Right after giving you the opportunity to receive a free high-resolution React cheatsheet by subscribing to my newsletter:

Get the PDF cheatsheet!

Exercise

To demonstrate, I’ve put together an example fiddle. The Enterprise component inside it is designed to meet the requirements which make shouldComponentUpdate a serious consideration; it does processing on the original value, it frequently re-renders, and its props contain immutable data.

To complete the exercise, all you need to do is create a copy of the project and follow the steps below.

Before getting started, make sure you know how to start your browser’s profiler. You can find instructions at the Chrome or Firefox websites.

Step 1: Measure the standard version

Let’s start by measuring the time spent in render when the user clicks an action:

  1. Open the JavaScript profiler for the example fiddle’s window
  2. Start recording
    Start recording
  3. Click the “Toggle synergy!” link and let the page cycle for ~5 seconds
    Toggle Synergy
  4. Stop recording by click the same button you used to start
  5. Find the most costly render method by sorting by “self” time — it should be near the top of the list. Write down the duration of time spent in it. For this you want is the”total” time, i.e. time spent in both the render function itself, as well as functions it calls.
    render

But what if the times spent in render are small enough that you can’t find it near the top of the function list? Great! You don’t need shouldComponentUpdate. Or at least not until you fix all your other performance issues.

Step 2: Measure the shouldComponentUpdate version

The next step is to measure the amount of time spent rendering once we add shouldComponentUpdate to the App component. Obviously, to do so you’ll need a shouldComponentUpdate. Here’s one I prepared earlier:

shouldComponentUpdate(nextProps, nextState) {
    return !Immutable.is(this.state.synergy, nextState.synergy)
},

Once you’ve added in shouldComponentUpdate, repeat the process in step 1. And once you’re done, finish off your results by finding and adding the time spent in shouldComponentUpdate to the time spent in render. Here are my results:

render with shouldComponentUpdate:

render with shouldComponentUpdate

shouldComponentUpdate:

shouldComponentUpdate

And there you have it! You now roughly know what kind of gain your component gets from shouldComponentUpdate. That is, if you get a gain at all.

But did you notice how I emphasised roughly? Make sure to keep in mind that this method is not all that precise – the actual results can vary a lot. If you don’t believe me, just try repeating the above and seeing how much they differ. But if the results are so inaccurate, how are you to ever make a good decision?

Seriously, the results vary a lot. This method will get you a rough idea, but please — don’t show this to a statistician.

Making a decision

At the risk of sounding abnormally shitsukoi

You only want to use shouldComponentUpdate if your measurements show you beyond any reasonable doubt that it makes sense.

What is beyond reasonable doubt? My experience has been that if adding shouldComponentUpdate halves your running time, it is probably actually providing a benefit — and not occurring by chance.

But with this said, keep in mind that the benefits you receive can only be as great as the original running time is long. If your original render function only takes on the order of 100ms, it is a tossup as to whether any benefits will be perceived at all.

At the end of the day, the decision has to be made in the context of the project you’re writing. But by keeping in mind the maintenance downsides, the measurement uncertainties and the ranges in which benefits can actually be perceived, you’ll be equipped to make the right decision. Of course, this all assumes that you’re measuring a well implemented shouldComponentUpdate in the first place.

Writing an effective shouldComponentUpdate

So you’ve found the perfect spot for a dirty little shouldComponentUpdate — but how do you write it?

Now I could just wave my arms and then proclaim “Immutable.js!” But then I imagine you’d feel a little like this:

How to draw an owl

The thing is, Immutable.js isn’t so much the solution as it is a (mindblowingly awesome) tool to build your own solution. And it isn’t the only tool either; plain ol’ Object.assign will get you most of the way there.

Ok. But if immutable state won’t solve your problems, what will?

I’m glad you asked! The real solution is well structured state. In fact, well structure state will solve more than just your shouldComponentUpdate implementation; it will fix all of your problems down to the Jehova’s Witnesses waking you up to preach to you even after moving to Japan. It is really that good.

But how do you structure your state well? That’s a great question, and is one I’m going to be answering in a couple posts time — right after my (nearly finished) Luddite’s guide to Redux State. Don’t want to miss it? Then subscribe! You’ve got nothing to lose. Actually, you stand to gain the high resolution PDFs of these five React and JS cheatsheets:

cheatsheets

I will send you useful articles, cheatsheets and code.

I won't send you useless inbox filler. No spam, ever.
Unsubscribe at any time.

One more thing – I love hearing your questions, offers and opinions. If you have something to say, leave a comment or send me an e-mail at james@jamesknelson.com. I’m looking forward to hearing from you!

Further Reading

1 Comment Should I use shouldComponentUpdate?

  1. Mark Erikson

    Just wanted to say how much I appreciate your articles. They are consistently well-written, clear, relevant, and informative.

    I’m at the point where I’m not learning anything new from them myself, but they serve as great tutorials for anyone still in the earlier stages of learning the React ecosystem.

    I keep a list of high-quality React and Redux articles over at https://github.com/markerikson/react-redux-links. I’ve got a couple of your articles in that list already, and this one is absolutely going in the “Performance” section.

    Thanks!

    Reply

Leave a Reply

Your email address will not be published.