— by Shantanu Patil
I still remember being a newbie to this world of front-end engineering and learning React. I was impressed with the ease with which I could build web apps using reusable components and JSX. It was amazing how fast React could update the UI. While learning React, I never really paid much attention to how React is working under the hood, or what the most common term when it comes to React i.e. Virtual DOM, actually means. I never really felt the need to understand the working of REACT as it was getting my job done seamlessly.
Life was good until there were pressing needs to work on a few performance issues and multiple re-renderings on our app. In my research on solving the performance issues, I started reading about the functionalities and algorithms that React uses under the hood and that really helped me in solving the problem.
Only if I had taken some efforts to really understand React in the beginning, it would have saved me a lot of time now. But again life is a series of “Only ifs” and we have to learn from our mistakes.
If you are a new developer just starting with React or just a curious person keen on understanding how React works, then this article is for you. Let’s get started.
Virtual DOM
Let’s first start with the most common term you hear when reading about React. Virtual DOM.
As the name suggests, Virtual DOM is not an actual DOM, but a representation of actual DOM which is kept in memory. It’s a representation of DOM nodes in a tree format which is just a plain JavaScript Object.
Let’s take an example of any JSX that you might find in the return statement of a React component
<div>
<h1 className="title">Want to learn about Virtual DOM?</h1>
<div className="button-container">
<Button title="Yes" />
<Button title="No" />
</div>
</div>
This JSX gets converted into a tree format like this.
{
type: 'div',
key: null,
ref: null,
props: {
children: [
{
type: 'h1',
key: null,
ref: null,
props: {
className: 'title',
children: 'Want to learn about Virtual DOM?'
}
},
{
type: 'div',
key: null,
ref: null,
props: {
className: 'button-container',
children: [
{
type: Button(),
props: {
title: 'Yes'
}
},
{
type: Button(),
props: {
title: 'No'
}
}
]
}
}
]
}
}
React traverses the JSX recursively from the parent element. In this case, the parent is a <div> element. You can see here that the children are defined in the props property of an element. This <div> element has two children, <h1> and <div> . <h1> element in turn has a text element (”Want to learn about Virtual DOM?”) which gets defined as a child to <h1> in the tree structure. And this is how the DOM is converted to a Virtual DOM with nested elements.
The type defined in the Virtual DOM, in general, refers to the type of HTML DOM element. If you have noticed in the example above, we have used a React Component <Button />. Now, this is not a conventional HTML element. Hence, React treats these components a little differently. If it’s a functional component, React directly calls the function with its assigned props and if it’s a class component, React creates a new instance of the class component and calls its render method.
The key and ref properties are the same props that you know are used in components. key is required while rendering a list of elements, which we will see later in the article and ref are the references we pass in any HTML DOM element to manipulate the element like for managing focus. You can learn more about refs here.
This is what a virtual DOM is in a broader sense. Every time a React component updates, React builds a new tree in memory. Creating Javascript objects in memory on every update is much faster than updating the actual DOM since DOM operations are expensive to perform. A React component updates whenever the state or the props of the component change.
Let’s look at how the concept of a Virtual DOM in React helps in making the UI re-rendering smoother, faster and with no extra development effort.
Reconciliation
After every component update, React creates a new Virtual DOM tree. It then compares the old tree with the new tree to register all the changes that need to be done to the real DOM. This process of generating a new tree and comparing is called Reconciliation.
React uses a Diffing algorithm to compare both trees to effectively update the UI in a minimum number of operations. This algorithm is quite complex but it is based on two assumptions:
- Two elements of different types will produce different trees.
- The developer can hint at which child elements may be stable across different renders with a key prop.
Let’s understand what this means.
Two Elements of Different Types will Produce Different Trees
React parses the tree using Breadth-first search (BFS). Whenever a node in a tree is changed, it destroys all the child nodes and reconstructs the tree.
<nav>
<Component />
</nav>
If the above JSX changes to
<div>
<Component />
</div>
React will destroy the Component and will construct a new tree. The Component will be unmounted and all of its states will be destroyed. A developer can be careful while building the components so as to avoid unnecessary destroying and reconstructing of components.
While comparing elements of the same type for eg.
<ul>
<li>Apples</li>
<li>Oranges</li>
<li>Banana</li>
</ul>
It just updates the className attribute in the node, instead of destroying and reconstructing the whole node. This doesn’t result in un-mounting of component.
The Developer can Hint at Which Child Elements May be Stable Across Different Renders with a key prop.
We come across lists in our React app quite often. It doesn’t have to be an HTML list element. List basically means mapping an array to render the elements. let’s take an example.
<ul>
<li>Apples</li>
<li>Oranges</li>
<li>Banana</li>
</ul>
Now in the next update we add a new element to the list.
<ul>
<li>Apples</li>
<li>Oranges</li>
<li>Banana</li>
<li>Watermelon</li>
</ul>
React will traverse through the list and check every element. It will check the first element Apples, it is same in both the trees so it doesn’t need to update this. This will go on until it finds a new element at the end of the list, watermelon. It will just append the new element to the list. This sounds straightforward.
Now what if we add the new element at the top of the list instead of at the bottom.
<ul>
<li>Apples</li>
<li>Oranges</li>
<li>Banana</li>
<li>Watermelon</li>
</ul>
React will traverse through the list and check that the first element is changed from Apples to Watermelon. It will reconstruct that element. The second element also changed from Oranges to Apples. React will again reconstruct the element. In the end it will end up reconstructing the whole list. Now, this becomes very expensive when the list size is in 1000s and the updates are happening every second.
To solve this problem keys are introduced.
<ul>
<li key="apple">Apples</li>
<li key="orange">Oranges</li>
<li key="banana">Banana</li>
</ul>
<ul>
<li key="watermelon">Watermelon</li>
<li key="apple">Apples</li>
<li key="orange">Oranges</li>
<li key="banana">Banana</li>
</ul>
React will now compare the keys and update the element whose keys are modified.
That’s the reason we see a warning to provide keys to list items that React throws.
The mistake people do and which I also have been guilty of doing for very long is providing the index of the array as a key.
list.map((item, index) => (
<li key={index}>{item}</li>
))
This is not a good practice as it may cause displaying of wrong data or performance issues.
After Registering what all needs to be updated, React syncs the virtual DOM with the real DOM, and that’s how we see UI getting updated.
Conclusion
Hope this explains how this “under the hood” Virtual DOM strategy in React helps applications re-render their UI faster. React has also re-implemented the Reconciliation architecture after React version 16 by introducing a new data structure called Fiber. But on a high level, it still follows the same principles. Will probably cover how React Fiber works in some other article.
Overall, I think with UI page load performance being very critical in this age, it is important for developers to understand and appreciate this concept. This also helps to avoid making mistakes while defining and using components, which might look inconsequential but can have significant impact on performance and how your users perceive your application.
Understanding the working principles of React has surely helped me in writing better React components and better code. Hope this article was helpful for all my fellow React Developers.
Call to Action
Feel free to comment or write to us in case you have any further questions at support@zipy.ai. We would be happy to help you. In case you want to explore Zipy for your app, you can sign up here or book a demo here.
Originally published at https://www.zipy.ai.