React VDOM in 8 minutes

React VDOM in 8 minutes

(Image created by Midjourney)

In this article, I am going to take a look at React. I used React for the first time a few weeks ago and while looking deeper into the inner workings of React I thought it might be a good idea to create a small blog post on the things I researched and learned. This helped me to stay focused on the most relevant concepts and also helped me to better internalize them. Therefore I hope this post can help you as well! This post will focus on the VDOM, Reacts implementation of the VDOM and the usage of the key property.

VDOM and DOM

You might have read that React makes use of the concept called virtual DOM (VDOM), but what exactly is the VDOM?

To understand the concept of the VDOM, let's take a look at the DOM first. When the browser reads the HTML of a webpage it creates a DOM tree. Each node in the tree represents part of the page e.g. a heading, paragraph or text.

Strictly speaking, the DOM is an interface for HTML and XML documents. With this interface, one can access and manipulate these documents by modifying, adding or deleting elements or content.

Why don't we use the DOM directly in React to manipulate content on a webpage?

When researching this topic I have read many other sources that state:

  • every DOM operation is expensive because after the DOM changes it needs to rerender

  • that the rendering part makes this process slow

Most resources claim that this is the reason we use the VDOM because the virtual DOM is just a JavaScript representation of the DOM and therefore can be updated without triggering a rerender.

I was a bit confused about these sources because, in the end, the VDOM needs to know what to update and eventually still uses the DOM to update the HTML. So how can this be an advantage over normal DOM manipulation? Doesn't this add just additional operations? Rich Harris the developer of Svelte exactly makes that point in his blog post "virtual DOM is pure overhead". He states:

"The only way it could be faster is if we were comparing it to a less efficient framework (there were plenty to go around back in 2013!)"

His post and another post by someone else both state that the advantage of React lies in the separation of state and DOM nodes, not necessarily the VDOM.

The other sources that I have read may be right as well, but currently, I don't see how. I am new to Front-End development, maybe there is something that I am missing. If someone can explain it to me, please leave a comment. In the end, I think it's not super important, so let's not get lost in the details for now and focus on the implementation of the VDOM in React.

How is the VDOM implemented in React?

As the documentation states the virtual DOM is "typically associated with React Elements" because they are representing the user interface. There are other objects called "fibers" that save additional information. This post will focus on React Elements, if you want to know more about fibers check out this post.

So what is a React Element?

A React Element is simply a JavaScript object that describes a component instance or a DOM node and its desired properties. (<-- Wow, exceptionally good explanation right there) Every element is either a component element or a DOM element. To better understand how these two types of elements look let's take a look at two examples.

Component Element

Let's say we have a website where we can see two texts by two different authors:

This website could be structured into the following components:

function App()
{
    return (
        <>
            <CustomContainer>
                <TextUnit title="Lorem Ipsum" text="Lorem ipsum dolo[...]"/>
                <AuthorAvatar imgSource={lenna} nameOfAuthor="Lenna"/>
            </CustomContainer>
            <CustomContainer>
                <TextUnit title="What is Latin?" text="Latin (lingua [...]"/>
                <AuthorAvatar imgSource={cameraman}
                              nameOfAuthor="Cameraman"/>
            </CustomContainer>
        </>
    );
}

These JSX expressions are internally translated to createElement method calls. Technically you could achieve the same result by calling the createElement method multiple times. You can try this out yourself with your examples with the babel online compiler here. The previous example will be compiled to:

function App() {
    return (
    React.createElement(React.Fragment, null, 
        React.createElement(CustomContainer, null, 
            React.createElement(TextUnit, {
                title: "Lorem Ipsum",
                text: "Lorem ipsum dolor sit amet, [...]"}),     
            React.createElement(AuthorAvatar, {
                imgSource: lenna, 
                nameOfAuthor: "Lenna"})
        ),
        React.createElement(CustomContainer, null, 
            React.createElement(TextUnit, {
                title: "What is Latin?",
                text: "Latin (lingua Lat\u012Bna, [...]"}), 
            React.createElement(AuthorAvatar, {
                imgSource: cameraman,
                nameOfAuthor: "Cameraman"}
                )
        )
    ));
}

To see the React element that is created by the createElement method calls in the above example we can log the <App> component.

console.log(<App/>) prints the following JavaScript object:

The type property indicates that this element describes a component instance, it also indicates that it is a functional component. (More on the properties of this object shortly.)

DOM Element

For comparison, let's have a look at an element describing a DOM node. <h1 color='red'>Hello World!</h1> is also a JSX expression and can again be written with the createElement method as React.createElement("h1", { color: "red" }, "Hello World!"). So it is an object just like the <App/> component element shown before.

console.log(<h1 color='red'>Hello World!</h1>) prints the following JavaScript object:

The type indicates that it's an element describing a DOM node.

Properties of the element object

Both component elements and DOM elements share the same properties. In the two examples, a few properties of the element object are not displayed. I left them out because I want to focus on the properties that you can set via JSX and properties that kind of make sense in this blog post. Let's take a quick look at these:

  • $$typeof: is a measure against XSS. Every element is expected to have this property. Because Symbols have a unique value and a Symbol with the same value cant be created React can spot elements that were not created by React

  • key: is a property that identifies an element among its siblings and can be set to improve the rendering of React elements (more on that shortly)

  • props: contains all information that can be passed down as input to child components. This property also contains the children of an element.

  • ref: can be used to persist data in between states without causing a rerender (if you want to know more take a look here)

  • type: gives the type of the element, either a function or a class for a component element or a string for a DOM element

If you worked with React before you for sure used the props property before, and probably you used some of the other properties before as well.

Element tree building process

Unfortunately, I was not able to find current information on how React builds the tree of DOM and components elements without wanting to spend unlimited time on the topic. Before React 18 the ReactDOM.render method kicked off a process called "top-down reconciliation" which is (or was?) a part of the reconciliation algorithm. In the top-down reconciliation, React gradually resolved the component elements with the help of the props property. If you want to know more about this process you can check out this post from 2014 here. Maybe this still works the same, maybe something got changed. If anybody knows something please let me know in the comments.

Reconciliation

After the tree is created, React creates a new tree when the state of the application changes. The diffing algorithm then compares the VDOM tree with the VDOM tree before the change and decides which DOM nodes need to be updated.

If you want to know how React performs this process called 'reconciliation' and how React avoids implementing an algorithm with the complexity O(n³) and instead implements an algorithm with the complexity O(n) by making some simplifications take a look at this post here.

What does the key property do?

The diffing algorithm is the reason we can use the key property to minimize the number of renders. Because the key property only exists because of the diffing algorithm and because the property was already mentioned before, I want to take this chance to shortly explain the usage of the property in more detail.

When React compares the children of two elements it will iterate over the items of the children one by one and compare them to each other. Let's take a look at the following example from the React documentation:

// old
<ul>
  <li>first</li>
  <li>second</li>
</ul>

// new
<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

In this example, React will compare <li>first</li> with the 'new' <li>first</li>, won't detect any changes and therefore won't make any updates. The same applies to the <li>second</li> element. The <li>third</li> will be inserted as a new element. If we take a look at another example we may spot a problem right away.

// old
<ul>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

// new
<ul>
  <li>Connecticut</li>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

React will compare <li>Duke</li> with <li>Connecticut</li> and think a change occurred. Therefore it will update the <li>Connecticut</li> and <li>Duke</li> elements and insert the <li>Villanova</li> element. When in fact just one element was added as the first list item. This problem can be avoided with the key property.

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

With the key property, React can identify elements among its siblings. In the example above React will know that only the <li key="2014">Connecticut</li> has been added and therefore won't change the other two unchanged elements. Only the new element will be added.

That's it for this post, I hope you could learn something! Additionally, if you want to get informed about future posts: sUbsCr1be t0 mY n3wslet1er!1