Author avatar

Chris Dobson

Profiling Performance with React Developer Tools

Chris Dobson

  • Jun 5, 2020
  • 9 Min read
  • 1,302 Views
  • Jun 5, 2020
  • 9 Min read
  • 1,302 Views
Web Development
Front End Web Development
Client-side Framework
React

Introduction

Profiling the performance of a React app can be a difficult and time consuming process. By installing the React developer tools, you can record and interrogate the performance of individual components in your app and make this process much easier.

The guide will be based on a React web app, however, the developer tools can also be used with a React Native app. The app used in the examples below is here.

Installation

The first thing to do is install the developer tools. How you do this depends on the browser you are using to run the app. For Chrome and Firefox, the developer tools are available as browser extensions and can be found in the appropriate extension store. If using a different browser, the developer tools can be installed as a standalone app from this npm package.

Once developer tools are installed in either the standalone app or the browser, two new tabs will be available: Components and Profiler. This guide will use the Profiler tab.

Profiler Tab

When you first select the Profiler tab, no profile information will be available. In order to retrieve profiling data, click the start profiling button Start profiling button and perform the operations in the application that have been exhibiting poor performance. Once you have finished, click the button again; it will be red now, indicating that a profile is being recorded. Once the recording has been completed, a number of different charts will be available that can be used to view how each component performed.

This is an example of a chart showing each commit that occurred during the profile recording:

Commits chart

Hover over a commit to get information on its duration and at what point in the recording it occurred, and select a commit to view it in the other charts.

Flamegraph

This is a flamegraph chart of a commit in the example app:

Flamegraph chart

Each bar in the graph represents a React component, and the width of each bar represents how long it took to render the component and its children. The bars are also color-coded to help show at a glance which components took the longest to render, but the actual durations can be seen on the bar (if there is room for the text) or by hovering on the bar.

In the graph above, you can see that the AppContextProvider took the longest to render with a total of 8.7 ms. However, only 1.8 ms of that was taken up by the AppContextProvider itself, while the other 6.9 ms was its children rendering. If you then move to its only child, the Context.Provider component, you can see that its render took 0.4 ms, and the rest of the 6.9 ms was taken by its children, and so on down the chart.

As you move down the chart, it can become more difficult to see the components as they get smaller. Selecting a component will expand that component to the full width of the chart, making it much easier to see.

Ranked Chart

The ranked chart displays each component in the commit as a bar, but this time they are ordered by the duration of their render. This is a ranked chart of the same commit as the flamegraph above:

Ranked chart

This chart deals just with the time taken by the component itself and doesn't show anything about how long the children took to render. If you are having a performance problem that is being caused by a single component, it should be very easy to identify using this chart.

Profiler Settings

There are two options on the Profiler tab in the developer tools settings menu:

Profiler settings

Often, components are rendered that we don't expect to have been, and debugging why this has happened can be very difficult and time consuming. To help with this, the React developer tools can record the reason each component was rendered when recording a profile. Check the Record why each component rendered while profiling checkbox to use this feature. If this feature is selected, when a component is selected from either of the charts in the right hand panel, a reason will be displayed as to why the component rendered.

When profiling an app, you will sometimes produce a very large number of commits, and you will often only be concerned with those that take a longer amount of time. In order to reduce the clutter in the Profiler tab, you can set the Hide commits below setting, and only commits that took longer than the specified number of milliseconds will be shown.

Profile the Initial Render

It is often the case that the initial render of an application is the one that takes longest and is therefore the one you will be most interested in profiling. But by using the method above to profile, you need to already be on the page and, therefore, have missed the initial render. To profile the initial render, you can use the reload and start profiling button Reload and start profiling, which will reload the page and start profiling before the initial render. You can then continue or stop recording as you normally would.

Find a Performance Issue

The example app contains a performance issue. While the issue is very contrived, it can be used to show how to find an issue in an app.

In the example app, when clicking on the Show count buttons on the right hand side of the window, there is a noticeable lag before the count appears and the button label changes to Hide count. To try and identify the cause of this lag, you could record a profile of a user toggling the Show count and Hide count buttons a few times. This is a chart of the commits produced by showing and hiding three times:

Commits of show and hide toggle 3 times

You can see that commits 1, 3 and 5 are showing bars significantly higher than the others. This would be expected as the lag occurs on the show rather than the hide. Selecting any one of these commits shows a flamegraph and a ranked chart like these:

Flamegraph chart of a poorly performing application

Ranked chart of a poorly performing application

The first thing you see is that all of the components above the NumberSelector in the tree are showing with a grey background because they were not rendered in the commit; this is what we would expect as all of the state and event handling for this component is done locally.

The other thing you can see is that of the 1199.3 ms that the entire commit took, 1194.1 ms was taken up rendering the NumberSelector, so it is almost certainly this component you should be looking at to identify the issue. If you know the codebase well, you will probably be able to go straight to the correct file for this component, but if you don't, you can use the React developer tools Component tab to help you out. First, select the NumberSelector component in either the flamegraph or ranked chart, and then go to the Components tab. The component you have selected in the chart will now be selected in this tab. You can then click on the view source button View source button to go straight to the source for the component in the browser developer tools. This will take you to withChangedCount.jsx, which is the higher order component that wraps every NumberSelector component in the app.

It probably won't take too much time to see that this component contains a function, getCountToShow(), which is called every time the component is rendered with the showingCount state set to true. That looks like this:

1
2
3
4
const getCountToShow = () => {
  pointlessDelay();
  return changedCount;
};
javascript

A good attempt to fix this performance issue might be to remove the call to pointlessDelay and then re-record a profile and see if the lag has disappeared both from the user's point of view and the profiler charts.

Conclusion

This guide has shown some of the tools that can be used to profile a React app and help identify performance issues. There is one more chart available in the profiler: the interactions chart. This uses an experimental API that is subject to change and would probably need a guide of its own to cover the functionality.

The code for the example app used in this guide can be found here.

13