Are you having trouble trying to debug react rerenders? Debugging unwanted react rerenders can be one of the most frustrating and tricky problems when building Reacts apps. Especially when working with large systems, trying to identify which prop or hook changes are triggering a re-render can be enough to drive anybody mad.
Why does React Rerender?
When working in React, there are a few scenarios in which a component will re-render. If you're not familiar with React yet, or you're just a beginner, consider checking out my tutorial on building a web app with React. For the sake of this article, I'm going to limit my discussion to simple functional components which use hooks.
React components re-render so that they can show the most up-to-date content on the screen when something changes. What will make an individual component re-render is when there's a change to the component's props, component state, or the parent has re-rendered. Sometimes, in order to avoid extraneous rerenders, we'll wrap out React component in React.memo which will memoize the function and only re-compute it if the props have changed.
If you're not careful, you can easily find yourself in a scenario where too many props and hooks are changing that it becomes infuriating to try and track down all the possible scenarios causing your component to re-render. Now, we really should strive to live in a world where your entire component tree can rerender fast enough that it doesn't matter if there are a few somewhat unnecessary calls being added here and there. However, sometimes we have such bulky components that it's not practical to try and render them constantly.
How can I Debug React Rerenders?
React devtools can be a great tool for you to use to gauge the performance of your app. It can be used to record sequences of events and show you how many renders occurred, allow you to step through each render, and it can even tell you the reason for the re-render (props changed, hooks changed, etc.). However, what it can't do is give you full insight into what the props were at each render cycle, which can be invaluable insight when you're trying to debug a complicated set of renders.
In order to do this, I use a custom hook like the one below which allows me to call it with my props, and will log the differences between my old and new props with each render.
function logPropDifferences(newProps, lastProps) { const allKeys = new Set(Object.keys(newProps)).add(Object.keys(lastProps)); allKeys.forEach(key => { const newValue = newProps[key]; const lastValue = lastProps[key]; if (newValue !== lastValue) { console.log('New Value: ', newValue);
console.log('Last Value: ', lastValue); } }); }
function useDebugPropChanges(newProps) { const lastProps = useRef(); // Should only run when the component re-mounts useEffect(() => { console.log('Mounted'); }, []); if (lastProps.current) { logPropDifferences(newProps, lastProps.current); } lastProps.current = newProps; }
This is an extremely useful debugging tool as it allows me to see exactly what is happening with my component at each render cycle.
In order to use this hook, I would simply call it on the first line of any react component or custom hook, taking care to pass in all of the props that the component or hook accepts so I can see the full picture. Using this hook, I can carefully analyze each step and can easily find which prop changes seem to be suspicious and unnecessary!
Tip: Look for object props that are logging as changes but identical. Typically, people forget that returning variables inside an object is actually creating a new object every time which will fail an equality test against previous props.
Similarly, a common source of re-renders is using () => {} will create a new function every time, so unless the component is a leaf node or has a fast rendering cycle, you may want to consider switching to useCallback instead.
That's it! Now you should have everything you need to figure out what is causing the source of those pesky rerenders. If there are tricks that you use that I didn't cover here, please leave a comment below!