Debug Why React (Re-)Renders a Component
React is known for its performance by using the Virtual DOM (VDOM). It only triggers an update for the parts of the real DOM that have changed. In my opinion, it is important to know when React triggers a re-rendering of a component to be able to debug performance issues and develop fast and efficient components.
After reading this article, you should have a good understanding of how React rendering mechanism is working and how you can debug re-rendering issues.
What is rendering?
First, we need to understand what rendering in the context of a web application means.
If you open a website in the browser, what you see on your screen is described by the DOM (Document Object Model) and represented through HTML (Hypertext Markup Language).
The W3C Document Object Model (DOM) is a platform and language-neutral interface that allows programs and scripts to dynamically access and update the content, structure, and style of a document.
DOM nodes are created by React if the JSX code is converted. We should be aware that real DOM updates are slow as they cause a re-drawing of the UI. This becomes a problem if React components become too big or are nested on multiple levels. Each time a component is re-rendered its JSX is converted to DOM nodes which takes extra computation time and power. This is where React's Virtual DOM comes into the game.
Virtual DOM
React uses a Virtual DOM (VDOM) as an additional abstraction layer on top of the DOM which reduces real DOM updates. If we change the state in our application, these changes are first applied to the VDOM. The React DOM library is used to efficiently check what parts of the UI really need to be visually updated in the real DOM. This process is called diffing and is based on these steps:
- VDOM gets updated by a state change in the application.
- New VDOM is compared against a previous VDOM snapshot.
- Only the parts of the real DOM are updated which have changed. There is no DOM update if nothing has changed.
More details about this mechanism can be found in React's documentation about reconciliation.
What causes a render in React?
A rendering in React is caused by
- changing the state
- passing props
- using Context API
React is extremely careful and re-renders "everything all the same time". Losing information by not rendering after a state change could be very dramatic this is why re-rendering is the safer alternative.
I created a demo project on StackBlitz which I will use in this article to demonstrate React's rendering behavior:
The project contains a parent component, which basically consists of two child components where one component receives props and the other not:
As you can see, we log a warning message in the console each time the component's render
function is called.
In our example, we use functional components and therefore the execution of the whole function is similar to the render
function of class components.
If you take a look at the console output of the StackBlitz demo, you can see that the render method is called three times:
- Render
Parent
component - Render
Child
even if it has no props - Render
Child
withname
value from state as prop
If you now modify the name in the input field we trigger a state change for each new value. Each state change in the parent component triggers a re-rendering of the child components even if they did not receive any props.
Does it mean that React re-renders the real DOM each time we call the render
function? No, React only updates the part of the UI that changed.
A render is scheduled by React each time the state of a component is modified. For example, updating state via the setState
hook will not happen immediately but React will execute it at the best possible moment.
But calling the render
function has some side-effects even if the real DOM is not re-rendered:
- the code inside the render function is executed each time, which can be time-consuming depending on its content
- the diffing algorithm is executed for each component to be able to determine if the UI needs to be updated
Visualize rendering
It is possible to visualize React's VDOM as well as the native DOM rendering in the web browser.
To show the React's virtual render you need to install React DevTools in your browser. You can then enable this feature under Components -> View Settings -> Highlight updated when component render
.
This way we can see when React calls the render method of a component as it highlights the border of this component. This is similar to the console logs in my demo application.
Now we want to see what gets updated in the real DOM, therefore we can use the Chrome DevTools. Open it via F12
, go to the three-dot menu on right and select More tools -> Rendering -> Paint flashing
:
Debug why a component rendered
In our small example, it was quite easy to analyze what action caused a component to render. In larger applications, this can be more tricky as components tend to be more complex. Luckily, we can use some tools which help us to debug what caused a component to render.
React DevTools
We can again use the Profiler of the React DevTools. This feature records why each component rendered while the profiling was active. You can enable it in the React DevTools Profiler tab:
If we now start the profiling, trigger a state change, and stop the profiling we can see that information:
But as you can see, we only get the information that the component rendered because of a state change triggered by hook but we still don't know why this hook caused a rendering.
Why did you render?
To debug why a hook caused a React component to render we can use the npm package Why Did You Render.
It monkey patches React to notify you about avoidable re-renders.
So it is very useful to track when and why a certain component re-renders.
I included the npm package in my demo project on StackBlitz, to enable it you need to enable it inside the Parent.jsx
component:
If we now trigger a parent re-rendering by toggling the "Toggle Context API" checkbox we can see additional console logs from the library:
The console output is:
As you can see from the output we get detailed information on what caused the re-rendering (for example if it was a prop or hook change) and which data were compared, for example, which props and state were used for the diffing.
Conclusion
In this article, I explained why React re-renders a component and how you can visualize and debug this behavior. I learned a lot while writing this article and building the demo application. I also hope that you got a better understanding of how React rendering works and that you now know how to debug your re-rendering issues.
In the future, I will write more about React, so follow me on Twitter to get notified about the latest articles.
How to Deploy a Heroku Backend to a Netlify Subdomain
On my main domain mokkapps.de I have deployed my private portfolio website. For different use cases, I want to have a Node.js backend deployed to a subdomain, e.g. api.mokkapps.de.
Run, Build & Deploy Stencil and Storybook From One Repository
I recently joined a project where the team used two separate Git repositories for their web components based on Stencil and Storybook. But the idea of Storybook is that the so-called "stories" live next to the components source code. Therefore, it made no sense to me to have those two tools in different repositories, and I combined them both in one repository.