Making renders faster with the React 16.5 profiler
Tags: react, performance, webdev, javascript
Published: 2018-09-25 22:00:09

---
title: Making renders faster with the React 16.5 profiler
published: true
description: React 16.5 recently shipped, which added support for some new Profiling tools. We recently used these tools to identify a major source of slow render performance.
tags: react, performance, webdev, javascript
canonical_url: https://faithlife.codes/blog/2018/09/react-profiler/
cover_image: https://files.logoscdn.com/v1/files/15184384/content.png?signature=JOKPwvQUyZi2r2PZ5qOgFqanTOE
---

React 16.5 recently shipped, which added support for some new Profiling tools. We recently used these tools to identify a major source of slow render performance.

Faithlife.com is a web application powered by React 16.3. The homepage consists of a reverse-chronological timeline of posts. We received some reports that interactions with posts (such as replying) caused the browser to lag, depending on how far down the post was on the page. The further down the page the post was, the more lag occurred.

After updating React to 16.5 on a local copy of Faithlife, our next step was to start profiling and capture what components were re-rendering. Below is a screenshot of what the tools showed us clicking the 'Like' button on any post:

![Slow renders screenshot](https://files.logoscdn.com/v1/files/14522904/content.png?signature=NmNdRI26PmGCtI8F6Z-3-o99Le4)

The blue blocks below NewsFeed show render being called on all the posts in the feed. If there were 10 items loaded, `NewsFeedItem` and all its children would get rendered 10 times. This can be fine for small components, but if the render tree is deep, rendering a component and its children unnecessarily can cause performance problems. As a user scrolls down on the page, more posts get loaded in the feed. This causes render to get called for posts all the way at the top, even though they haven't changed!

This seemed like a good time to try changing `NewsFeedItem` to extend `PureComponent`, which will skip re-rendering the component and its children if the props have not changed (a shallow comparison is used for this check).

Unfortunately applying PureComponent was not enough - profiling again showed that unnecessary component renders were still happening. We then uncovered two issues preventing us from leveraging PureComponent's optimizations:

## First roadblock: Use of children props.

We had a component that looked something like this:

```jsx
<NewsFeedItem contents={item.contents}>
  <VisibilitySensor itemId={item.id} onChange={this.handleVisibilityChange} />
</NewsFeedItem>
```

This compiles down to:
```js
React.createElement(
  NewsFeedItem,
  { contents: item.contents },
  React.createElement(VisibilitySensor, { itemId: item.id, onChange: this.handleVisibilityChange })
);
```

Because React creates a new instance of `VisibilitySensor` during each render, the `children` prop always changes, so making `NewsFeedItem` a `PureComponent` would make things *worse*, since a shallow comparison in `shouldComponentUpdate` may not be cheap to run and will always return true.

Our solution here was to move VisibilitySensor into a render prop and use a bound function:

```jsx
<NewsFeedItemWithHandlers
  contents={item.contents}
  itemId={item.id}
  handleVisibilityChange={this.handleVisibilityChange}
/>

class NewsFeedItemWithHandlers extends PureComponent {
  // The arrow function needs to get created outside of render, or the shallow comparison will fail
  renderVisibilitySensor = () => (
    <VisibilitySensor
      itemId={this.props.itemId}
      onChange={this.handleVisibilityChange}
    />
  );

  render() {
    <NewsFeedItem
      contents={this.props.contents}
      renderVisibilitySensor={this.renderVisibilitySensor}
    />;
  }
}
```

Because the bound function only gets created once, the same function instance will be passed as props to `NewsFeedItem`.

## Second roadblock: Inline object created during render

We had some code that was creating a new instance of a url helper in each render:

```jsx
getUrlHelper = () => new NewsFeedUrlHelper(
	this.props.moreItemsUrlTemplate,
	this.props.pollItemsUrlTemplate,
	this.props.updateItemsUrlTemplate,
);

<NewsFeedItemWithHandlers
	contents={item.contents}
	urlHelper={this.getUrlHelper()} // new object created with each method call
/>
```

Since `getUrlHelper` is computed from props, there's no point in creating more than one instance if we can cache the previous result and re-use that. We used [`memoize-one`](https://github.com/alexreardon/memoize-one) to solve this problem:

```jsx
import memoizeOne from 'memoize-one';

const memoizedUrlHelper = memoizeOne(
	(moreItemsUrlTemplate, pollItemsUrlTemplate, updateItemsUrlTemplate) =>
		new NewsFeedUrlHelper({
			moreItemsUrlTemplate,
			pollItemsUrlTemplate,
			updateItemsUrlTemplate,
		}),
);

// in the component
getUrlHelper = memoizedUrlHelper(
	this.props.moreItemsUrlTemplate,
	this.props.pollItemsUrlTemplate,
	this.props.updateItemsUrlTemplate
);
```

Now we will create a new url helper only when the dependent props change.

## Measuring the difference

The profiler now shows much better results: rendering NewsFeed is now down from ~50ms to ~5ms!

![Better renders screenshot](https://files.logoscdn.com/v1/files/14522909/content.png?signature=FzQeT-m0XOUSU2hSJ7x2cYMqNlo)

## PureComponent may make your performance worse

As with any performance optimization, it's critical to measure the how changes impact performance.

`PureComponent` is not an optimization that can blindly be applied to all components in your application. It's good for components in a list with deep render trees, which was the case in this example. If you're using arrow functions as props, inline objects, or inline arrays as props with a `PureComponent`, both `shouldComponentUpdate` *and* `render` will always get called, because new instances of those props will get created each time! Measure the performance of your changes to be sure they are an improvement.

It may be perfectly fine for your team to use inline arrow functions on simple components, such as binding onClick handlers on `button` elements inside a loop. Prioritize readability of your code first, then measure and add performance optimizations where it makes sense.

## Bonus experiment
Since the pattern of creating components just to bind callbacks to props is pretty common in our codebase, we wrote a helper for generating components with pre-bound functions. Check it out [on our Github repo](https://github.com/Faithlife/react-util/tree/dcdeed8e2b24562766150661aaae8cfcd07c2de8/src/withEventHandlers).

You can also use windowing libraries, such as [react-virtualized](https://github.com/bvaughn/react-virtualized) to avoid rendering components that aren't in view.

Thanks to Ian Mundy, Patrick Nausha, and Auresa Nyctea for providing feedback on early drafts of this post.

Cover photo from Unsplash: https://unsplash.com/photos/ot-I4_x-1cQ