In our latest Writer’s Room blog – articles created by Andela Community members – Sheriff Olowolagba discusses “React Render”, the rendering/re-rendering process for Javascript’s popular library React. From virtual DOM to chess board comparisons, Sheriff explores the render and commit phases of React, and much more.
React is a popular Javascript library that builds user interfaces efficiently. One of its key features is the virtual Dom, which I would define as React’s copy of the actual DOM. The virtual DOM is especially important in the efficient rerendering (update) of the browser UI, since it makes it possible for React to rerender only components that need to be updated.
At the end of this article, you should have an understanding of the React render lifecycle, and know how to write optimized code. You should also be able to fix performance issues that occur due to unnecessary rerender.
Imagine we’re playing a game of chess over the network. You have a virtual representation of the board, your pieces, and mine on your device. I also have them on my device. Remember that they are in sync. When I move a piece on my device, all that is needed is an update of the piece’s location on your device (not a new board or rearranging the entire piece). That was me defining virtual DOM.
Another thing you’ll need to understand is the React rendering/rerendering process. Using our chess game example, the initial render is starting a new game. We get a board, get all the pieces, and place them correctly on the board while the rerender (update) is updating the position of a piece.
The rendering process consists of two phases
- Render Phase
- Commit Phase
The render phase is where React performs its magic. During the initial render, React cascades through the entire DOM tree, converts your components (JSX) into React Elements (Javascript object that describes your UI), and finally passes it to the Commit phase where it is written to the DOM.
During the Rerender (update), React looks for only components that have been flagged for an update, converts them into React elements, compare them with the previous React element, and passes to the commit phase only if the element has changed.
Question 1: What happens if the element does not change?
Nothing, React bails out of the cycle. This is known as “unnecessary render”.
Question 2: If the element did not change, why was it flagged as needing an update?
A React component is flagged for update (rerender) if any of the following happens:
- State change: if the state of the component is changed either via useState or useReducer.
- Props change: if props passed down from its parent change
- Parent change: when parents rerender, the children would also rerender even without props.
Now that we understand the virtual DOM, render process, unnecessary render, and things that cause rerender, let’s discuss a few ways to optimize our application render process.
Same Element Reference
Same Element Reference is a simple and powerful way of optimizing your app render process without additional operational overhead. It simply means that provided the reference to an in the virtual dom has not changed, react would skip re-rendering.
Below we will examine two similar applications, each with parent and child components, and explain how they handle update flags and rerender.
In the example above, the child component would always rerender whenever the count increased. This behavior is so because the child component is directly nested in the parent component. Remember that “parent change” is one of the causes of flagging a component for rerender (change) as we discussed above.
To address this issue, let’s rewrite this code.
In the above example, we moved the child from being invoked in the parent’s JSX to being passed as a prop. This trick is simply an application of the React fundamental “a component can change its state not its props”. Hence, React would reference the same element, rather than rerender knowing that the state change in the parent component cannot cause a prop (child component) to change. If this understanding of React is used deliberately, we would have solved a huge portion of unnecessary rerender.
The same element reference is especially useful when using React context, since you do not want your entire app to rerender unnecessarily every time a value changes in your context provider.
React.memo, PureComponent, shouldComponentUpdate, etc.
We can also optimize rerendering by shallowly comparing props and states before re-rendering.
Question: Why don’t we just use React.memo or PureComponents everywhere?
First, you need to understand that React render is not a bad thing, and React is very efficient at it. Also, the process of comparing has a cost. You should only memoize if the cost of rerenders is significant and you’re sure that our comparison would payoff by actually preventing rerender.
I’ll end this tutorial with a tweet by Dan Abramov:
This article is inspired Codevolution’s — React Render series
Want to be part of a vibrant community? Then join the Andela Talent Network!
If you found this blog useful, check out our other blog posts for more essential insights!