Understanding How React Renders With the Virtual DOM

To understand how React renders a user interface (UI), we first need to understand how a web page is served and presented in the browser. When the browser requests a web page, the server responds with an HTML document. The browser then translates the HTML into a tree-like representation of that page’s elements. Each HTML element corresponds to a node in the tree. This tree-like structure is known as the Document Object Model (or DOM). The browser then takes the DOM and paints those elements onto the screen. This becomes the UI that users see and interact with:

Let’s take the structure of this HTML as an example:

The above HTML would be represented in the DOM as:

As with any modern application, Javascript needs a way to interact with the elements of a page. Fortunately, the DOM provides APIs to allow Javascript to query, access, and manipulate elements in the DOM.

When working with React, our components' state and data are constantly changing. As actions trigger an element to change, the DOM will re-render that element along with all of it's child elements. All of these changes eventually have an effect on the page’s performance and can lead to a slow experience for users. Luckily, the Virtual DOM is a pattern that provides a more efficient way to handle these changes.

The Virtual DOM

React acts as a declarative abstraction that tells the actual DOM about the current state that should be rendered in the UI. We write React components in JSX and let the virtual DOM be responsible for handling any state changes in a performant way. The virtual DOM is a lighter replica of the actual DOM that is saved in the browser's memory. It comes with extra functionality that allows these re-render calculations to be made in a way that minimizes the changes needed for the actual DOM.

There are two phases that make up the lifecycle of the virtual DOM: rendering and reconciliation.

Rendering

When the page is initially rendered, React creates a virtual DOM in the browser's memory that represents the UI:

As soon as a change in state occurs, React immediately creates another snapshot of the virtual DOM:

Reconciliation

At this point, React uses a special diffing algorithm known as reconciliation to determine exactly what was changed. React does this by starting at the very top of the tree-like structure (the root) and works it's way all the way down, checking against every node between the two snapshots for any differences.

When React finds a difference, it relies on a rendering library (such as ReactDOM), to communicate with the actual DOM to only re-render the elements that have changed:

Efficiently Handling Lists

Often times, you'll encounter the need to render lists, like in the example from earlier:

If we were to add a new movie to the end of the list, React would traverse the virtual DOM, starting from the top, and determine that no changes are required until arriving at the very bottom of the list where it would append the new movie.

However, in the case that we needed to add a movie to the middle of the list, React would need some way to understand that we're targeting a specific position in the list, without re-rendering all the other movies after that:

To handle this in a fast and efficient way, React provides a unique key prop. We can provide each of the elements with their own key, which allows React to identify which specific elements needs changing and only applying a re-render for that element:

Conclusion

React makes use of a pattern known as the virtual DOM as a way to efficiently update elements in a page's actual DOM.The virtual DOM acts as an in-memory representation of the actual DOM, to allow us to only update specific elements, removing the need re-render more than what's necessary in order to deliver fast, performant user experiences.

Previous
Previous

Measuring React Performance With the Profiler

Next
Next

Simplifying & Sharing Code with React’s Custom Hooks