Understanding React Fiber Architecture: From React 15 to React 16
React 16 feels smoother than React 15 because its new Fiber architecture breaks state updates into small, priority‑aware units that the Scheduler runs asynchronously, allowing high‑priority user input to render first while lower‑priority work is paused and resumed, eliminating the lag seen in full‑tree re‑renders of React 15.
This article explains why React 16 feels smoother than React 15 by comparing two examples with a list of 5,000 items whose text changes as the user types. The React 15.7 example shows noticeable lag, while the React 16.8 example runs fluidly.
To understand the improvement, the article first reviews the browser rendering pipeline: browsers refresh at 60 Hz (≈16.6 ms per frame) and have a single main thread that runs JavaScript and performs layout/painting. JavaScript execution and DOM rendering are mutually exclusive, so long‑running JS blocks UI updates.
In React 15, every call to this.setState triggers a full re‑render of the component tree. Updating a list with 10,000 items forces a massive diff operation on the main thread, causing UI jank and delayed user input.
React 16 introduces the Fiber architecture. State updates are split into small units and scheduled asynchronously. High‑priority updates (e.g., user input) are processed first, while lower‑priority work can be paused and resumed, similar to an OS time‑slice scheduler.
The new architecture adds a Scheduler layer on top of the existing Reconciler and Renderer layers:
Scheduler – assigns priorities and decides when a task should run.
Reconciler – finds changed virtual DOM nodes and marks them.
Renderer – applies the marked changes to the real DOM.
React abandoned the native requestIdleCallback API due to compatibility, precision, debugging, and priority‑handling issues, and instead implements its own polyfill within the Scheduler.
Key code snippets illustrate the core ideas:
function workLoopConcurrent() {
// Perform work until Scheduler asks us to yield
while (workInProgress !== null && !shouldYield()) {
workInProgress = performUnitOfWork(workInProgress);
}
}Fiber nodes are the fundamental units of work. Each Fiber stores static information (type, tag, elementType, stateNode, parent/child/sibling links) and dynamic information (pendingProps, memoizedProps, updateQueue, lanes, effectTag, etc.).
function FiberNode(tag, pendingProps, key, mode) {
// static properties
this.tag = tag; // FunctionComponent, ClassComponent, HostComponent, ...
this.key = key; // React element key
this.elementType = null; // DOM tag name or component class
this.type = null; // Function or class instance
this.stateNode = null; // DOM node or component instance
this.return = null; // parent Fiber
this.child = null; // first child Fiber
this.sibling = null; // next sibling Fiber
this.index = 0; // child index
this.ref = null; // ref attribute
// dynamic properties
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
this.effectTag = NoEffect;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
this.lanes = NoLanes;
this.childLanes = NoLanes;
this.alternate = null; // link to the other version of this Fiber (double buffering)
}React maintains two Fiber trees simultaneously (double buffering): the current tree that is rendered on screen and a work‑in‑progress tree built in memory. After the work‑in‑progress tree is committed, it becomes the new current tree.
currentFiber.alternate === workInProgressFiber;
workInProgressFiber.alternate === currentFiber;During the initial mount, ReactDOM.render creates a fiberRoot and a rootFiber . The rootFiber becomes the entry point of the work‑in‑progress tree, which is then reconciled and committed to the DOM.
function App() {
const [num, addNum] = useState(0);
return (
number
addNum(num + 1)}>{num}
);
}
ReactDOM.render(
, document.getElementById('root'));When the user clicks the <span> , a new render phase creates a fresh work‑in‑progress tree, reuses unchanged Fibers from the current tree, and finally swaps the trees so the updated UI appears.
In summary, React 16’s Scheduler and Fiber together provide:
Fine‑grained priority scheduling.
Interruptible, asynchronous rendering.
Improved responsiveness for user interactions.
These enhancements deliver smoother page loads and a more fluid user experience.
HelloTech
Official Hello technology account, sharing tech insights and developments.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.