Mark Pollmann's blog

Improving React Performance

Introduction

This post is supposed to give a quick overview on how to avoid the biggest performance gotchas. Without further ado let’s get right into it.

Use the production build

Sounds funny but some companies use the development build in production without knowing. The dev build is considerably slower by adding all those checks for mistakes we know and love. The bundle size is of course bigger as well.

Performance Measuring with the Profiler

React 16.5 added support for a new React Profiler in the browser DevTools. Find it by clicking the Profiler tab next to Elements. With it you can record a slice of your app in action and analyse the resulting flamegraph and flip through the accumulated commits (basically render steps). The linked React blog post should contain everything you need to start using it, if you have any questions shoot me an email.

Avoiding unnecessary re-renderings

The central idea that React brought to the table is that the UI is a function of the state of the application. If a component receives props or updates its internal state it automatically starts a new render. React by default doesn’t check if the new input might actually be the same and thus wouldn’t have to update anything.

unnecessary re-rendering of your components is the one thing that makes React look slow if you’re not careful. Fortunately there are easy options to avoid this problem.

ShouldComponentUpdate

The life-cycle method shouldComponentUpdate(nextProps, nextState) lets you decide when you want the component to update. Simply compare nextProps and nextState with this.props and this.state and decide when it should update by returning true in that case.

PureComponent

If you don’t need this fine-tuned setup and just want to shallow compare incoming state and props you can let your component inherit from React.PureComponent which does it for free:

class MyComponent extends React.PureComponent {
  // [...]
}

Remember that two objects are always unequal when compared with ===, so if you pass an object as prop it will trigger a re-render, even if its key-value pairs didn’t change.

React.memo()

Lifecycle methods and PureComponent unfortunately are only available for class components. If you write function components you were out of luck. They always re-render when they receive new props. React 16.6 introduced the higher-order function (HOC) memo to mitigate this. Just wrap your component with this HOC and it does the same shallow compare of props as PureComponent does:

import React, {memo} from react

const MyComponent = memo(props => {
    // [...]
})

If you need more control memo takes a second argument, a function with the following signature: areEqual(prevProps, nextProps). Return true if you want to prevent the re-render.

Why Did You Update?

Why did you update is a nice little library you can use in development (React 16+ only) to watch for unnecessary updates. Simply add it via npm or yarn, monkey-patch your React instance and you get logs to your console when it suspects a re-render was useless.

Use immutable data structures

Libraries like immutable.js offer data structures that once created cannot be updated in-place. Instead a new instance is created. While this sounds quite inefficient, modern JavaScript VMs (like those powering FireFox, Node and Chrome) are really good at minimising this overhead. What we get in exchange is extremely quick variable comparisons. Just what we need for React’s diffing mechanism.

Avoid expensive functions in render

Because on every call to the component’s render function it will be re-run again.

render() {
    const sth = expensiveCalculation();
    return (
        // [...]
    )
}

If the calculation dynamically depends on input props or state it’s more difficult to avoid but if it’s not try running it once during mount or even outside of the component.

Windowing

If you have large lists of data of which only a small part is being shown at any point in time (think tables, lists, spreadsheets, drop-down menus, calendars, date-pickers, tree views, slideshows, news feed, chat) consider the windowing technique. What this means is rendering only the currently visible part and reusing the DOM elements once the user scrolls or clicks.

Let’s say you have a chat box where only 5 messages are visible (partially or fully). Then there are really only 5 containers (let’s say divs) in the DOM and not one for every message until the beginning of time, which could be hundreds or more.

For more information look into react-virtualized or the newer react-window . Both are written by React core team member Brian Vaughn .

Redux performance

If you use Redux in your application you have to be careful not to re-run every selector when a tiny part of your Redux state updates. A library that can help with this is Reselect . It creates memoized selectors to prevent the unneeded calculations.

Wrap-up

React is incredibly fast with updating the DOM in response to changes in app state. Still, you should watch out for unnecessary work it is doing, because nothing is faster than not doing any work at all.