What is Reconciliation in React?

Reconciliation is the core process behind React’s fast rendering, enabling it to update the user interface efficiently. This article explains what reconciliation is, how the Virtual DOM works, and the diffing algorithm React uses to update the real DOM with minimal performance overhead.

The Virtual DOM and Reconciliation

In traditional web development, directly manipulating the Real DOM (Document Object Model) is slow and resource-intensive. React solves this by keeping a lightweight representation of the UI in memory, known as the Virtual DOM.

When a component’s state or props change, React creates a new Virtual DOM tree. Reconciliation is the process of comparing this new Virtual DOM tree with the previous one to figure out the most efficient way to update the Real DOM. This comparison is done using a highly optimized “diffing” algorithm.

How the Diffing Algorithm Works

Comparing two trees of elements normally has a complexity of \(O(n^3)\), which is too slow for real-time UI updates. To make this fast, React applies a heuristic algorithm with \(O(n)\) complexity based on two main assumptions:

  1. Two elements of different types will produce different trees.
  2. The developer can hint at which child elements may be stable across renders with a key prop.

React implements these assumptions through specific rules during the diffing process.

1. Elements of Different Types

If the root elements of two trees have different types (for example, changing a <div> to a <span>, or a <Counter> to a <Header>), React will tear down the old tree and build the new tree from scratch. * Any state associated with the old components is destroyed. * The lifecycle methods for unmounting and mounting are triggered.

2. DOM Elements of the Same Type

When comparing two React DOM elements of the same type (for example, two <div> elements), React looks at the attributes of both. It keeps the underlying DOM node and only updates the attributes or CSS styles that have changed.

<!-- Old Element -->
<div className="before" title="stuff" />

<!-- New Element -->
<div className="after" title="stuff" />

In this example, React will only modify the className on the DOM node, leaving the title attribute untouched.

3. Component Elements of the Same Type

When a component updates, the instance remains the same, meaning state is maintained across renders. React updates the props of the underlying component instance to match the new element and triggers a re-render.

Recursing on Children and the Role of Keys

By default, when recursing on the children of a DOM node, React iterates over both lists of children at the same time and generates a mutation whenever there’s a difference.

If you append an element at the end of a list, the transition is efficient:

<ul>
  <li>First</li>
  <li>Second</li>
</ul>

<!-- New Tree -->
<ul>
  <li>First</li>
  <li>Second</li>
  <li>Third</li> <!-- React inserts this easily -->
</ul>

However, if you insert an element at the beginning, React will mutate every child because it compares the old first child with the new first child, finds a mismatch, and rebuilds the rest of the list.

Solving List Inefficiencies with Keys

To solve this performance bottleneck, React supports the key attribute. When children have keys, React uses them to match children in the original tree with children in the subsequent tree.

<ul>
  <li key="a">First</li>
  <li key="b">Second</li>
</ul>

<!-- New Tree -->
<ul>
  <li key="c">Third</li>
  <li key="a">First</li>
  <li key="b">Second</li>
</ul>

Because of the unique keys a, b, and c, React knows that the elements with keys a and b have simply moved, and it only needs to insert the new element with key c at the top.

Summary

Reconciliation is the engine that allows React developers to write declarative UI code without worrying about manual DOM manipulation. By using the Virtual DOM and a smart diffing algorithm, React calculates the exact set of changes needed to update the screen, ensuring web applications remain highly performant.