Ever since I started using react, I've always heard about making sure your app doesn't perform unnecessary state changes that would cause the components within your app to re-render excessively. But how do you know if your components are re-rendering when they shouldn't and why should you care if they are?
Identifying component re-renders
There are a number of different ways you can identify if your components are re-rendering more than they should be. Some common ways of doing this include adding console.log
statements into your components or using the react devtools extension to capture a profile and then analyzing it. Both are great ways to find re-renders hidden within an app, but log statements might not catch DOM updates and capturing profiles and analyzing them is tedious and requires a much deeper dive. I've recenetly come across react-scan which makes the job of highlighting re-renders so incredibly quick and easy. Here's a few different ways you can use it.
You can inspect any website using react-scan
by just running it as a CLI command. One downside of using this method is that it does require Chrome (I prefer to use Firefox).
npx react-scan@latest http://localhost:3000
Injecting the react-scan
script into the head of your app is another way of easily using it.
<head>
<script src="https://unpkg.com/react-scan/dist/auto.global.js" async />
</head>
If you don't want to use the CLI but you're on a site that you can't control and want to see if they're unnecessarily re-rendering their components, you can inject the script like so in the devtools console.
const script = document.createElement('script');
script.src = 'https://unpkg.com/react-scan/dist/auto.global.js';
document.head.appendChild(script);
Regardless of how you load react-scan
you can benefit from its simple yet intuitive way of highlighting components that re-render and providing an optional, audible cue each time a render occurs.
Finding and fixing re-rendering problems in this blog
After adding the react-scan
script to this blog, I went through each of the blog posts I've made thus far to see if there were any areas that were rendering more than they should. Most of the blog was fine, but I came across an issue with the Day 9 blog post as seen in the gif below. The start/stop button and map wrapper were being updated multiple times a second even though there was no need for them to be. They really only need to be updated whenever a user would either start or stop the animation.
Digging through the implementation, I found that I was keeping track of the band index using the useState
hook. This meant that every 150 milliseconds, I was updating state in the component that would cause it to re-render. Instead of using state here, I decided to switch to useRef
to keep track of what the current band index, this prevented the component from re-rendering every 150ms since the useRef
hook is intended to keep track of information not necessary for DOM rendering.
Other ways of fixing re-renders may including using the useMemo
hook. This is a useful utility provided by react to cache the results of a calculation. Based on the dependencies passed into the hook, react will decide whether or not the result needs to be recalculated which could prevent extra renders.
Why should you care if re-renders are happening?
Although you should aim to eliminate re-renders throughout your applications when possible, they're not all bad. Having a tiny component like the one I highlighted above re-render might not comsume much in terms of resources, especially if there are no DOM updates being performed. However, if the component was a wrapper at the top level of the page, we'd end up re-rendering all of its children components too. This can lead to sluggish or even unresponsive UIs.
I haven't spent the time to dig too deeply into understanding when DOM updates occur or what the actual performance impacts of a re-render might be, but react-scan
has become a tool that I've started using to understand if I'm committing a react crime somewhere in my app. You should give it a try.