Optimize React Performance

How to improve React performance in production and avoid common mistakes

Did you know that using key={} incorrectly in a list can increase DOM operations by over 10x? React is known to be blazingly fast with its Virtual DOM implementation. However, even with React’s built-in performance, there are instances when the UI can begin to feel sluggish. The primary culprit for poor performance is generating too many renders and reconciliations. We’ll cover 4 techniques to help ensure that your app is optimized and offers a snappy user experience.

  1. Use key correctly
  2. Manage shouldComponentUpdate
  3. Extend PureComponent instead of Component
  4. Build for production

Check out gitconnected >

The community for developers and software engineers

Measure Performance

Before digging into the code, it’s essential to understand what you’re optimizing and the impact that it makes. The two tools that I would suggest using are Lighthouse and react-addons-perf. Both tools can be installed through npm (1, 2) and both offer a Chrome extension (1, 2). I personally recommend using npm for Lighthouse because the results seem to be more reliable and use the Chrome extension for React Perf. Below is an example of the output from from Lighthouse, and we’ll look at a result from React Perf in the following section.

Note: Lighthouse is available natively through Chrome starting in version 60, and react-addons-perf will no longer be compatible with React 16.o+, but the following results are still valid for any version of React.

Use Key={} Correctly

The key attribute is used by React to uniquely identify an element in a list at any point during the life of the application. If the DOM is updated, React is able to identify which items have changed. Thus, the key value on a component must be unique and consistent in relation to its sibling components in a list. This is one area that newer React developers tend to slip up on, but React developers of any experience level may not truly understand the impact of using the key attribute correctly when generating a list. Two common mistakes are using the index during a map(element, index) or using Math.random() to generate a key. The best solution is a UUID associated with the list item, but any unique identifier will do. The typical scenario you will use a keyed list is the following.

Since the user ID is unique, we can confidently use it as the key for the list item. For a more practical application, I created a mock version of the Postmate’s feed that removed a store from the DOM when clicked.

Using the react-addons-perf Chrome extension, I measured the number of operations to remove one element using a UUID as a key and also one using Math.random() as a key. The results show that using a unique and consistent key require only 28 operations to remove the item whereas a randomly generated key requires over 25x the number operations to perform the exact same task. Be smart with your keys.

Manage shouldComponentUpdate

Managing the component lifecycle allows you to specify exactly when a component should render. Since you as developer have knowledge about your application that React does not, you’re in the position to determine if the component actually needs to re-render. Rendering and reconciliation are the most expensive operations in React, so eliminating any unnecessary invocations based on your domain knowledge can yield huge improvements for your performance and user experience.

Use React.PureComponent instead of React.Component

React provides PureComponent to manage the shouldComponentUpdate lifecycle hook for the developer. PureComponent will do a shallow comparison of all props/state and will only render if any of them change. Most of the time you’re able to get away with using PureComponent instead of handling it yourself. It does come with some careful considerations that can lead to nasty bugs. JavaScript uses pointers when assigning an object to a variable. This means that the variable points to a specific place in memory and not to anything specific about the object itself. If you update a property on the object, the pointer will remain unchanged and therefore the shallow comparison won’t recognize the update. To avoid this, you could 1) pass variables directly to the component instead of inside an object or 2) embrace immutable data structures.

The following example using PureComponent will achieve the same result as our example using shouldComponentUpdate.

One final consideration is that functional components aren’t optimized for React yet, but the team has promised to do so in the future. If you’ve identified an area in your application that needs optimization, you’re better off using a class component to ensure you’re getting the required performance.

Obligatory Dan Abramov quote

Build for Production

Building for production may seem obvious, but it’s essential that you inspect your process to ensure it compiles using NODE_ENV = 'production'. React includes a number of warnings to help you debug which are stripped away in production. These warnings make development more friendly, but also they make the React build larger and slower. The production React build renders 2–8x faster than the development build.

The following Webpack plugins will provide the desired production build:

The DefinePlugin ensures that the NODE_ENV is set correctly, andUglifyJsPlugin compresses the JavaScript. The transform-react-constant-elements moves constant elements to a higher scope, and transform-react-inline-elements replaces the native React.createElement() with a more optimized version. Note that these should only be used in production because they will generate cryptic errors and make debugging difficult in development. You can use the use the React Developer Tools for Chrome to assess if you’re using the production build of React.

If you found this article helpful, please tap the 👏. Follow me for more articles on React, Node.js, JavaScript, and open source software! You can also find me on Twitter or gitconnected.