Virtual DOM: ReactJS implementation
How does the Virtual DOM work? And what is actually the DOM? If you ever have wondered about those questions, this post is for you.
When I first started developing web apps I discovered a lot of new concepts.
For example, DOM. What is that, I wondered. As time went by I realized that I had started using it before even knowing about its existence.
So, what is it?
- It stands for Document Object Model.
- It's a tree of nodes that represents a structured document (i.e. HTML).
- It exposes an interface to interact with its nodes. We can create, edit and even delete them.
- We can inspect it using the famous F12's elements tab in the browser.
- The browser creates it every time we open an HTML document.
When should we use the DOM's interface?
Whenever we want to:
- Create a better user experience.
- Manipulate the HTML elements in real time.
- Avoid refreshing the page with every user interaction.
The last point is really important. Did you know that everytime you refresh the page, the browser recreates the entire DOM again?.
Imagine a button that, once pressed, only changes a small part of the page. For example, a hamburger button that displays a sidebar menu. The new recreated DOM would be really similar to the original, the only difference being the new sidebar menu.
is it really necessary to recreate the whole DOM again? Wouldn't it be better to just recreate that small sidebar sub tree?
Ok. This idea might sound familiar. Ever heard of jQuery?. jQuery uses the DOM's interface to provide us, developers, a more friendly interface to manipulate it, allowing us to create a more dynamic web page. But there are a few situations in which interacting with the DOM’s interface can get messy.
Weak points of using the DOM’s interface
What if we are interacting a lot with the DOM to recreate lots of subtrees?, I mean, lots of I/O synchronic interactions. That means that a lot of elements are being drawn and its style is being reapplied over and over again. Take into account that if we want to show a new side menu, we don’t only need to add those into the DOM, but also remove the ones that were there in the first place. The browser recalculates the css, does a layout process and then a repaint. The performance gets worse and worse as the tree grows.
What if we need to constantly query elements from the DOM? Bad performance, again. The user experience gets worse.
So, we now know that recreating the whole DOM is not a good idea, and interacting directly with it can lead to underperformance in some cases.
That's why the concept of Virtual DOM came up.
Virtual DOM, what is it about?
It's also a tree, like a light copy of the DOM, where every node is a JS object.
It’s the representation of the UI in a specific moment in time.
It's a technique, and there are a few known implementations.
How does it work?: “Diffing and patching”
Every time something changes, a new virtual DOM is created.
The difference between the new and the old virtual DOM is calculated.
The real DOM is patched with that difference.
ReactJS virtual DOM
On this particular implementation, every node is an immutable instance of a JS class called ReactElement. Their immutability makes them easier to be compared.
You might have wondered: if these nodes are JS elements, does that mean that JS's reserved words can’t be used? Well, you’re right!
That's the reason why, what before looked like:
in React, looks like this:
class can't be used because it's a reserved word. Instead, className needs to be used.
There are other cases that you can check out here.
How does ReactJS manage its Virtual DOM?
The common way is to have components. Every component has a related state object, a setState function to modify it, and listeners of that function.
What happens when there is a change on a component?
- A listener gets notified about it, and triggers an update process
- A new virtual DOM is generated.
- A reconciliation is done through diffing algorithms. This step is executed to know exactly what is different between the new and the old virtual DOM.
- The original DOM is patched.
It's important to mention that not only a change on the state can trigger an update process, a change on the component's props can cause that too.
The red nodes are the ones that change in the new virtual DOM. The reconciliation process should catch those and treat them as affected nodes.
Taking a look at the “Affected nodes” tree, we can see that there are more red nodes than expected: the children of the nodes that were marked as different in the new virtual DOM are also there.
We might wonder: Why are the children nodes also red? Does this mean that the whole tree should be rendered again?.
To answer that, lets check the Diffing algorithm basic rules:
- If the types of the elements are different, the subtree is rendered again.
- There is an assumption that the elements should have an unique key.
The first rule only considers elements of different types. What if the elements are of the same type?
- The attribute is changed on the original node.
- The process is done all over again for every subtree.
Let’s try to understand the second rule with an example:
Suppose we have a simple unordered list, with two items, and we want to add a third one (‘Charly’)
What happens without keys?
It compares “Charly” with “Pink Floyd”, as they’re not the same, they’re repainted.
It compares “Pink Floyd” with “Divididos”, as they’re not the same, they’re repainted.
It adds “Divididos” at the end.
Bottom line: it repaints everything.
Now with keys:
It realizes that “Pink Floyd” and “Divididos” were moved, because it checks the keys, and it doesn’t do a repaint.
So, now that we have a better understanding of how a Virtual DOM works, what are the pros?
Virtual DOM’s Pros
- Reading a JS node that we own on the virtual DOM is faster than reading the DOM.
- No more manual node manipulation.
- Only updates what's necessary on the DOM.
- The repaint process only occurs once.
It’s not that the DOM is slow. The advantage of using a virtual DOM is that we only update the necessary nodes. And we do that in only one step, updating the DOM just once per frame. We get better performance. And when I say “we”, I really mean “react’. React does that for us. And, of course, another advantage is that the virtual DOM makes our developer experience happier. And we all know that happier developers write happier code with a happier outcome.
Hope you found this info useful. Thanks for reading!