Interview notes on React Reconciliation (Virtual DOM)
It's been a while since I had to recall the reasons why React uses Virtual DOM. I was recently asked this in an interview, and I did an okay job explain it but not to the extent I wanted. So I spent some time re-reading the official documentation and wrote down some notes. I hope these will be helpful for anyone preparing for a React interview. Also, I have called out the vital part of each section to make it easier to see the key points.
What is it?
Virtual DOM is the virtual representation of all the elements on the page in Javascript.
Virtual DOM, like the name suggests, is a virtual JavaScript representation of the actual page DOM. All the elements which exist on the page also exist in this virtual representation. When React code detects a change, it will perform a differentiation (AKA diff) by comparing the before and after virtual DOM objects. The diff algorithm will compute a list of DOM manipulations that it deems necessary to bring the actual DOM in line with the virtual DOM. As a result, the user will see the page changing when they interact with it (or changing in the background automatically if that's the intended behaviour like a stock ticker site).
Why virtual DOM?
React uses Virtual DOM because it makes page updates more performant when there are many elements on the page. Since it could figure out which nodes need to be updated, then go and do that.
The virtual DOM makes it easier to handle changes on the page, displaying a basic page layout or handling complex DOM changes. It only re-renders the part of the DOM that changed (we'll cover how in the Diff Algorithm section below) whilst leaving the rest intact. Therefore it ensures page element update speed even when there are tens of thousands of elements on the page since only a tiny subset of the total number of elements in the DOM requires updating.
Virtual DOM allows developers to focus on what needs to happen rather than how it happens. By handling DOM changes for the developer automatically, it makes writing web applications much faster.
Virtual DOM isn't a silver bullet
Like many solutions, React Virtual DOM shines in some scenarios, specifically complex DOM tree structures that are deep and wide.
It's worth noting that React's virtual DOM only has an edge in performance if the DOM tree structure is both deep and wide. Virtual DOM isn't faster 100% of the time.
In small applications written in vanilla, JavaScript could find an element with getElementById
and update it with much less time and resource. However, the vanilla way is an old school way of writing web applications, mainly because it isn't scalable in larger projects. For one, it is not a realistic expectation that developers will assign every DOM element an id
when working in large applications. If we don't do this, then finding a part becomes more computationally expensive. Not to mention the development overhead with assigning and recalling id
for each element on the page.
Angular also abstracted away from the complications of manual DOM updates but doesn't utilise Virtual DOM. In comparison, in smaller and more straightforward applications, React's Virtual DOM wouldn't contribute to much performance gain and might negatively impact it. Instead, it shines in more significant codebases.
Tradeoffs
The React team highlighted two main problems:
Need to ensure element types (e.g. div to a span ) only changes when necessary since React will blindly reconstruct all the nodes under that element.
Altering a component's type might be very costly, especially if they have identical nested nodes since React virtual DOM will reconstruct all the nodes under the changed element.
Must provide keys to all the list item to ensure the O(n) performance.
Using non-unique and reliable keys could result in poor performance where the O(n) performance isn't guaranteed. See in the next section on why this is the case.
Diff Algorithm
Imagine the virtual DOM as a tree structure. The algorithm will start from the root node then traverse through the whole tree. React checks for two things to see if an element has changed: Is the element type different? If the type has changed, then tear down and reconstructive all of the nodes under this element. If nothing has changed, then continue. If the element properties/attributes changed? When a component updates, the instances remain the same so that the state is maintained across renders.
Update new props on the underlying component instance, and call render() to regenerate the sub-tree
FAQ
Why are keys used in React lists?
Keys can reduce recursive elements lists to only require a single parsing O(n)
to update the whole tree. Whereas without using keys, the diff and update process is more computationally expensive O(n^3)
.
By default, React will assign an item's list index as the key. However, this only partially solves the problem, it works when the list remains the same, and just one element's properties are updated. When elements get added/removed, their indexes change to end up with a similar issue.
<ul>
<li>Duke</li>
<li>Villanova</li>
</ul>
<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
React asks for a manual key assigned to lists items to reduce the unnecessary computation of the indexes on the page when it changes.