DEV Community

Neha Malvia
Neha Malvia

Posted on

Part 1 — The Era of the Stack Reconciler

React 15 was defined by the Stack Reconciler. During this era, the update process was synchronous and uninterruptible. Once a state update started, React was like a high-speed train with no brakes; it couldn't pause, even if the tracks were blocked.

The High-Level Process
React maintains an in-memory tree (the Virtual DOM) that represents your component hierarchy. When an update occurs, React creates a new Virtual DOM, compares it with the previous version, and calculates the minimum number of changes needed to update the Real DOM.


Deep Diving into the Reconciliation Process
To understand why this architecture eventually hit a ceiling, we need to look at how it handled updates internally.

React update flow diagram

1. The Trigger: this.setState()
The moment a component calls this.setState(), it’s a "go" signal. React immediately begins the process of figuring out what changed for that specific component and its entire subtree.

2. The Recursive "Dive"
To understand the UI changes, React calls the render() method. Because components are nested, this triggers a recursive chain:

  • If Component A has a child Component B, React calls A.render(), then immediately dives down to call B.render().
  • Each of these function calls is pushed onto the JavaScript Call Stack.

The Crucial Constraint: JavaScript is single-threaded. If the Call Stack is occupied by React’s recursion, the browser cannot handle user inputs like scrolling or clicking a "Cancel" button. The main thread is "held hostage" until the stack is completely empty.

3. Generating the Virtual DOM
As the recursion moves down the tree, React produces a brand-new Virtual DOM. This is a lightweight, in-memory JavaScript object representing the "desired" state of the UI at that exact moment.

4. Synchronous Diffing & Immediate DOM Patching
This is the most critical part of the React 15 architecture. In the Stack Reconciler, Diffing (finding the difference) and Patching (updating the screen) happened simultaneously.

As the algorithm walked the tree and found a difference—for example, a <div> changing to a <span>—it would immediately call document.setAttribute or appendChild to update the Real DOM right then and there.

Because React was mutating the Real DOM while still calculating the rest of the tree, the process was impossible to pause. You couldn't stop halfway because you had already started changing what the user sees on their screen!


The Performance Bottleneck
Because these four steps happened in one unbroken chain of execution, the browser was effectively "locked" until the very last component in the tree was processed. If your component tree was large, this led to two major issues:

  • "Jank" and Dropped Frames: To maintain a smooth 60fps, a browser has about 16.6ms to paint a frame. If reconciliation took 30ms, the browser would skip a frame, leading to visible stuttering or "jank."
  • Delayed User Interactions: If a user typed into an input field while React was busy reconciling a large list, the keystroke wouldn't appear on the screen until the entire tree was finished.

The Realization
React engineers realized that Simultaneous Diffing and Patching was a dead end for complex, highly interactive applications. To move forward, they needed to achieve two things:

  1. Decouple the "Math" (Diffing) from the "Writing" (Patching).

  2. Enable Pausing: The ability to stop the "Math" if the user does something more important.

This realization set the stage for the most ambitious rewrite in React's history: Fiber. In Part 2, we will dive deep into how Fiber completely re-engineered the internals of React to make the web feel more fluid than ever before.

Top comments (0)