Alex Sidorenko AvatarAlex Sidorenko

6 Oct 2021

How to Detect Slow Renders in React?

Improving React app performance often comes down to finding bottlenecks and fixing them. One well-placed memoization can make a slow app fast again. But how do we detect these bottlenecks?

Profile the problem

Open the React Developer Tools Profiler tab. Click the record button to start profiling. Interact with the part of your app that feels slow, then click the record button again to stop profiling.

Analyze the results

Find a slow commit you want to improve. You can see the commits bar in the top right corner of the profiling results. For more information on commits, check out React Docs - Browsing Commits.

In our case, the 1st, 2nd, and 4th commits are slow. They take more than 300ms to render. Any response to a user action that takes more than 100ms breaks the connection between the action and the result (RAIL: A User-Centric Model For Performance).

Now let's pick one of these commits and check the "Flamegraph" to see what's causing this poor performance.

The Flamegraph shows our component tree. We can see that the Home component and its entire sub-tree re-render. The SearchResults component responsible for the main UI change is pretty fast and takes only 7.4ms to render. The SlowComponent takes up most of the rendering time. It is the bottleneck.

Fix the bottleneck

Let's look at the code of the SlowComponent:

const SlowComponent = () => {
// Expensive calculation that takes 300+ms
const n = [...Array(3000000).keys()].reduce((p, c) => p + c);

return <p>Expensive calculation - {n}</p>;
};

We can wrap our expensive calculation with useMemo to ensure it only runs when necessary. Since we don't rely on any props, we can leave the dependency array empty. This way, our expensive calculation won't be re-triggered every time SlowComponent re-renders.

const SlowComponent = () => {
const n = useMemo(() => {
// Expensive calculation that takes 300+ms
return [...Array(3000000).keys()].reduce((p, c) => p + c);
}, []);

return <p>Expensive calculation - {n}</p>;
};

Now let's analyze the performance again.

The UI feels faster already. Let's check the commits.

The 1st, 2nd, and 4th commits are still the slowest. But now they each take around 12-17ms to render, which is 14 times faster than before. Let's analyze the Flamegraph to see what happened.

The SearchResults component now takes the most time to render. But since it's only 12ms, we have nothing to worry about. And now that we've put our memoization in place, the SlowComponent takes only 0.3ms to render.


Follow me on 饾晱 for short videos about Next.js

饾晱 Follow for more