A comprehensive, line-by-line implementation of React 18 core functionality from scratch, demonstrating internal working principles through modular design.
| Section | Description | Key Topics |
|---|---|---|
| π Getting Started | Setup and basic usage | Installation, Development, Examples |
| ποΈ Architecture Overview | High-level system design | Module structure, Design patterns |
| β‘ Core Implementation | Detailed implementation flows | Startup, Rendering, Reconciliation |
| π£ Hooks System | Complete hooks implementation | useState, useEffect, useReducer |
| π DOM Diff Algorithm | Reconciliation and diff process | Element comparison, List reconciliation |
| π Concurrent Features | React 18 concurrent capabilities | Suspense, Transitions, Priority |
| π― Event System | Synthetic event implementation | Delegation, Dispatch, Handlers |
| β° Scheduler | Task scheduling and prioritization | Time slicing, Priority queues |
| π§ Advanced Topics | Deep implementation details | Error handling, DevTools, Performance |
| π Reference | API reference and examples | Code samples, Best practices |
Before diving into this React 18 implementation, ensure you understand:
- JavaScript ES6+: Modern JavaScript features and patterns
- Data Structures: Binary Operations, Min Heap
- Algorithms: Depth-First Traversal
- Web APIs: MessageChannel API
# Clone and install
git clone <repository-url>
cd mini_React
npm install
# Start development
npm run dev
mini_React/
βββ π src/ # Source implementation
β βββ π react/ # Core React package
β βββ π react-dom/ # DOM renderer
β βββ π react-reconciler/ # Reconciliation engine
β βββ π scheduler/ # Task scheduler
β βββ π shared/ # Shared utilities
βββ π markdown/ # Detailed documentation
βββ π README.md # This comprehensive guide
This React 18 implementation follows these core principles:
- π Modular Architecture: Clear separation of concerns across packages
- β‘ Concurrent Rendering: Non-blocking, interruptible rendering
- π― Priority-Based Scheduling: Intelligent task prioritization
- π Fiber-Based Reconciliation: Efficient tree traversal and updates
- π£ Hook-Driven State: Functional component state management
graph TB
A[React Core] --> B[React DOM]
A --> C[React Reconciler]
C --> D[Scheduler]
B --> E[DOM Bindings]
E --> F[Event System]
C --> G[Fiber Architecture]
G --> H[Hooks System]
style A fill:#61dafb
style C fill:#ff6b6b
style D fill:#4ecdc4
style G fill:#45b7d1
| Package | Responsibility | Key Files |
|---|---|---|
| react/ | Public API & Hooks | React.js, ReactHooks.js |
| react-dom/ | DOM Rendering | ReactDOMRoot.js |
| react-reconciler/ | Core Engine | ReactFiberWorkLoop.js, ReactFiber.js |
| scheduler/ | Task Management | Scheduler.js, SchedulerMinHeap.js |
| shared/ | Utilities | ReactSymbols.js, ReactFeatureFlags.js |
// src/main.jsx - Application Entry Point
import * as React from "react";
import { createRoot } from "react-dom/client";
function App() {
const [count, setCount] = React.useState(0);
return <div onClick={() => setCount(count + 1)}>Count: {count}</div>;
}
const root = createRoot(document.getElementById("root"));
root.render(<App />);The React application execution involves multiple interconnected phases, each with specific responsibilities and detailed internal processes.
sequenceDiagram
participant Browser as Browser
participant Main as main.jsx
participant React as React Package
participant ReactDOM as ReactDOM Package
participant Shared as Shared Utils
participant Reconciler as Reconciler
participant Scheduler as Scheduler
Browser->>Main: Load Application
Main->>React: import * as React
React->>Shared: Load ReactSymbols
React->>Shared: Load ReactFeatureFlags
React->>React: Initialize ReactCurrentDispatcher
React->>React: Initialize ReactCurrentBatchConfig
React->>React: Setup Hook Dispatchers
Main->>ReactDOM: import { createRoot }
ReactDOM->>Reconciler: Load Reconciler APIs
ReactDOM->>Scheduler: Load Scheduler APIs
ReactDOM->>ReactDOM: Register Event Plugins
ReactDOM->>ReactDOM: Initialize DOM Bindings
Detailed Module Loading Process:
// Phase 1.1: React Core Initialization
function initializeReactCore() {
// Step 1: Initialize global dispatchers
ReactCurrentDispatcher.current = null;
ReactCurrentBatchConfig.transition = null;
// Step 2: Setup development tools integration
if (__DEV__) {
ReactDebugCurrentFrame.setExtraStackFrame = null;
}
// Step 3: Initialize shared internals
const ReactSharedInternals = {
ReactCurrentDispatcher,
ReactCurrentBatchConfig,
ReactCurrentOwner,
ReactDebugCurrentFrame,
};
return ReactSharedInternals;
}
// Phase 1.2: Event System Initialization
function initializeEventSystem() {
// Register all supported events
registerSimpleEvents();
registerTwoPhaseEvent('onBeforeInput', ['beforeinput', 'compositionend']);
registerTwoPhaseEvent('onCompositionEnd', ['compositionend']);
registerTwoPhaseEvent('onCompositionStart', ['compositionstart']);
registerTwoPhaseEvent('onCompositionUpdate', ['compositionupdate']);
// Initialize event priorities
setCurrentUpdatePriority(DefaultEventPriority);
}flowchart TD
A[createRoot called] --> B[Validate Container]
B --> C[Create FiberRoot]
C --> D[Create HostRoot Fiber]
D --> E[Initialize Update Queue]
E --> F[Setup Event Delegation]
F --> G[Create ReactDOMRoot Instance]
G --> H[Mark Container as Root]
subgraph "FiberRoot Creation Details"
C1[Allocate FiberRootNode]
C2[Set containerInfo]
C3[Initialize pendingLanes]
C4[Setup callback queues]
C5[Initialize ping cache]
end
C --> C1 --> C2 --> C3 --> C4 --> C5
style A fill:#61dafb
style G fill:#4ecdc4
Detailed Root Creation Implementation:
// Phase 2.1: Container Validation and Preparation
export function createRoot(container, options) {
// Step 1: Validate container element
if (!isValidContainer(container)) {
throw new Error('createRoot(...): Target container is not a DOM element.');
}
// Step 2: Process options
const {
identifierPrefix,
onRecoverableError,
transitionCallbacks,
} = options || {};
// Step 3: Create internal root structure
const root = createContainer(
container,
ConcurrentRoot,
null,
isStrictMode,
concurrentUpdatesByDefaultOverride,
identifierPrefix,
onRecoverableError,
transitionCallbacks
);
// Step 4: Mark container for React
markContainerAsRoot(root.current, container);
// Step 5: Setup event delegation system
const rootContainerElement = container.nodeType === COMMENT_NODE
? container.parentNode
: container;
listenToAllSupportedEvents(rootContainerElement);
// Step 6: Return ReactDOMRoot instance
return new ReactDOMRoot(root);
}
// Phase 2.2: Fiber Root Structure Creation
function createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks) {
// Step 1: Create root node
const root = new FiberRootNode(containerInfo, tag, hydrate, hydrationCallbacks);
// Step 2: Create host root fiber
const uninitializedFiber = createHostRootFiber(tag, isStrictMode);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
// Step 3: Initialize update queue
const initialState = {
element: initialChildren,
isDehydrated: hydrate,
cache: null,
transitions: null,
pendingSuspenseBoundaries: null,
};
uninitializedFiber.memoizedState = initialState;
const updateQueue = createUpdateQueue();
uninitializedFiber.updateQueue = updateQueue;
return root;
}sequenceDiagram
participant App as Application Code
participant Root as ReactDOMRoot
participant Reconciler as Reconciler
participant Scheduler as Scheduler
participant WorkLoop as Work Loop
participant Fiber as Fiber Tree
participant DOM as DOM
App->>Root: root.render(<App />)
Root->>Root: Clear container innerHTML
Root->>Reconciler: updateContainer(element, root)
Reconciler->>Reconciler: Create Update Object
Reconciler->>Reconciler: Enqueue Update
Reconciler->>Reconciler: scheduleUpdateOnFiber()
Reconciler->>Scheduler: ensureRootIsScheduled()
Scheduler->>Scheduler: Calculate Priority
Scheduler->>Scheduler: scheduleCallback()
Scheduler->>WorkLoop: performConcurrentWorkOnRoot()
WorkLoop->>WorkLoop: prepareFreshStack()
WorkLoop->>WorkLoop: renderRootConcurrent()
loop Work Loop Iteration
WorkLoop->>Fiber: performUnitOfWork()
Fiber->>Fiber: beginWork()
Fiber->>Fiber: completeWork()
WorkLoop->>Scheduler: shouldYield()?
end
WorkLoop->>Reconciler: finishConcurrentRender()
Reconciler->>Reconciler: commitRoot()
Reconciler->>DOM: Apply DOM Changes
Detailed Render Process Implementation:
// Phase 3.1: Update Container Process
function updateContainer(element, container, parentComponent, callback) {
const current = container.current;
const eventTime = requestEventTime();
const lane = requestUpdateLane(current);
// Step 1: Create update object
const update = createUpdate(eventTime, lane);
update.payload = { element };
if (callback !== undefined && callback !== null) {
update.callback = callback;
}
// Step 2: Enqueue the update
const root = enqueueUpdate(current, update, lane);
if (root !== null) {
// Step 3: Schedule the update
scheduleUpdateOnFiber(root, current, lane, eventTime);
entangleTransitions(root, current, lane);
}
return lane;
}
// Phase 3.2: Work Loop Execution
function workLoopConcurrent() {
// Work until scheduler tells us to yield
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
function performUnitOfWork(unitOfWork) {
const current = unitOfWork.alternate;
// Step 1: Begin work phase
setCurrentDebugFiberInDEV(unitOfWork);
let next;
if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {
startProfilerTimer(unitOfWork);
next = beginWork(current, unitOfWork, renderLanes);
stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
} else {
next = beginWork(current, unitOfWork, renderLanes);
}
resetCurrentDebugFiberInDEV();
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) {
// Step 2: Complete work phase
completeUnitOfWork(unitOfWork);
} else {
// Step 3: Continue with child
workInProgress = next;
}
ReactCurrentOwner.current = null;
}flowchart TD
A[Component Render] --> B{Component Type?}
B -->|Function Component| C[Setup Hook Context]
B -->|Class Component| D[Call Lifecycle Methods]
C --> E[renderWithHooks]
E --> F[Set Current Dispatcher]
F --> G[Execute Component Function]
G --> H[Process Hook Calls]
H --> I{Hook Type?}
I -->|useState| J[Mount/Update State]
I -->|useEffect| K[Schedule Effect]
I -->|useCallback| L[Memoize Callback]
I -->|useMemo| M[Memoize Value]
J --> N[Return JSX]
K --> N
L --> N
M --> N
D --> O[componentDidMount/Update]
O --> N
N --> P[Reconcile Children]
P --> Q[Continue Fiber Tree]
style C fill:#61dafb
style H fill:#4ecdc4
Hook Execution Flow Details:
// Phase 4.1: Hook Context Setup
export function renderWithHooks(current, workInProgress, Component, props, secondArg, nextRenderLanes) {
renderLanes = nextRenderLanes;
currentlyRenderingFiber = workInProgress;
// Step 1: Clear previous hook state
workInProgress.memoizedState = null;
workInProgress.updateQueue = null;
workInProgress.lanes = NoLanes;
// Step 2: Set appropriate dispatcher
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
// Step 3: Execute component function
let children = Component(props, secondArg);
// Step 4: Handle re-renders during render phase
if (didScheduleRenderPhaseUpdateDuringThisPass) {
let numberOfReRenders = 0;
do {
didScheduleRenderPhaseUpdateDuringThisPass = false;
localIdCounter = 0;
if (numberOfReRenders >= RE_RENDER_LIMIT) {
throw new Error('Too many re-renders...');
}
numberOfReRenders += 1;
currentHook = null;
workInProgressHook = null;
workInProgress.updateQueue = null;
ReactCurrentDispatcher.current = HooksDispatcherOnRerender;
children = Component(props, secondArg);
} while (didScheduleRenderPhaseUpdateDuringThisPass);
}
// Step 5: Cleanup
ReactCurrentDispatcher.current = ContextOnlyDispatcher;
currentlyRenderingFiber = null;
currentHook = null;
workInProgressHook = null;
renderLanes = NoLanes;
return children;
}sequenceDiagram
participant Reconciler as Reconciler
participant BeforeMutation as Before Mutation
participant Mutation as Mutation Phase
participant Layout as Layout Phase
participant Passive as Passive Effects
participant DOM as DOM
participant Browser as Browser
Reconciler->>Reconciler: commitRootImpl()
Note over Reconciler: Phase 1: Before Mutation
Reconciler->>BeforeMutation: commitBeforeMutationEffects()
BeforeMutation->>BeforeMutation: Process getSnapshotBeforeUpdate
BeforeMutation->>BeforeMutation: Schedule useEffect cleanup
Note over Reconciler: Phase 2: Mutation
Reconciler->>Mutation: commitMutationEffects()
Mutation->>DOM: Insert/Update/Delete DOM nodes
Mutation->>DOM: Update DOM properties
Mutation->>DOM: Update refs
Reconciler->>Reconciler: Switch fiber trees (root.current = finishedWork)
Note over Reconciler: Phase 3: Layout
Reconciler->>Layout: commitLayoutEffects()
Layout->>Layout: Call componentDidMount/Update
Layout->>Layout: Call useLayoutEffect
Layout->>Layout: Update refs
Note over Reconciler: Phase 4: Passive Effects
Reconciler->>Passive: Schedule passive effects
Passive->>Passive: Call useEffect callbacks
Passive->>Passive: Call useEffect cleanup
Layout->>Browser: Trigger browser paint
Detailed Commit Implementation:
// Phase 5.1: Complete Commit Process
function commitRootImpl(root, renderPriorityLevel) {
// Step 1: Prepare for commit
const finishedWork = root.finishedWork;
const lanes = root.finishedLanes;
if (finishedWork === null) {
return null;
}
root.finishedWork = null;
root.finishedLanes = NoLanes;
// Step 2: Before mutation phase
const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(root, finishedWork);
// Step 3: Mutation phase
commitMutationEffects(root, finishedWork, lanes);
// Step 4: Switch the fiber tree
root.current = finishedWork;
// Step 5: Layout phase
commitLayoutEffects(finishedWork, root, lanes);
// Step 6: Schedule passive effects
if (
(finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||
(finishedWork.flags & PassiveMask) !== NoFlags
) {
if (!rootDoesHavePassiveEffects) {
rootDoesHavePassiveEffects = true;
pendingPassiveEffectsRemainingLanes = remainingLanes;
scheduleCallback(NormalSchedulerPriority, () => {
flushPassiveEffects();
return null;
});
}
}
// Step 7: Cleanup and prepare for next render
remainingLanes = mergeLanes(remainingLanes, getRemainingLanes(root, lanes));
ensureRootIsScheduled(root, now());
return null;
}sequenceDiagram
participant User as User Interaction
participant DOM as DOM Element
participant EventSystem as Event System
participant Reconciler as Reconciler
participant Scheduler as Scheduler
participant Component as Component
participant Hooks as Hook System
User->>DOM: Click/Input/etc
DOM->>EventSystem: Native Event Captured
EventSystem->>EventSystem: Create Synthetic Event
EventSystem->>EventSystem: Find Target Fiber
EventSystem->>EventSystem: Collect Event Listeners
EventSystem->>EventSystem: Dispatch Event
EventSystem->>Component: Call Event Handler
Component->>Hooks: setState/dispatch called
Hooks->>Hooks: Create Update Object
Hooks->>Reconciler: Schedule Update
Reconciler->>Scheduler: ensureRootIsScheduled()
Scheduler->>Scheduler: Determine Priority
Scheduler->>Reconciler: Schedule Render
Note over Reconciler: Trigger Re-render Cycle
Reconciler->>Reconciler: Start Work Loop
Reconciler->>Component: Re-render Component
Component->>DOM: Apply Changes
Detailed Event Handling Process:
// Phase 6.1: Event Capture and Processing
function dispatchEventForPlugins(domEventName, eventSystemFlags, nativeEvent, targetInst, targetContainer) {
const nativeEventTarget = getEventTarget(nativeEvent);
const dispatchQueue = [];
// Step 1: Extract events from fiber tree
extractEvents(
dispatchQueue,
domEventName,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
// Step 2: Process dispatch queue
processDispatchQueue(dispatchQueue, eventSystemFlags);
}
// Phase 6.2: State Update Scheduling
function dispatchSetState(fiber, queue, action) {
const lane = requestUpdateLane(fiber);
const eventTime = requestEventTime();
// Step 1: Create update object
const update = {
lane,
action,
hasEagerState: false,
eagerState: null,
next: null,
};
// Step 2: Eager state calculation optimization
if (fiber.lanes === NoLanes && (fiber.alternate === null || fiber.alternate.lanes === NoLanes)) {
const lastRenderedReducer = queue.lastRenderedReducer;
if (lastRenderedReducer !== null) {
try {
const currentState = queue.lastRenderedState;
const eagerState = lastRenderedReducer(currentState, action);
update.hasEagerState = true;
update.eagerState = eagerState;
if (Object.is(eagerState, currentState)) {
// Bail out - no change needed
return;
}
} catch (error) {
// Suppress errors, will be caught during render
}
}
}
// Step 3: Enqueue update and schedule work
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
if (root !== null) {
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
entangleTransitionUpdate(root, queue, lane);
}
}flowchart TD
A[Update Triggered] --> B{Update Priority?}
B -->|Sync| C[Immediate Execution]
B -->|User Blocking| D[High Priority Queue]
B -->|Normal| E[Normal Priority Queue]
B -->|Low| F[Low Priority Queue]
B -->|Idle| G[Idle Priority Queue]
C --> H[Synchronous Render]
D --> I[Concurrent Render with High Priority]
E --> J[Concurrent Render with Normal Priority]
F --> K[Concurrent Render with Low Priority]
G --> L[Idle Render]
I --> M{Should Yield?}
J --> M
K --> M
L --> M
M -->|No| N[Continue Work]
M -->|Yes| O[Yield to Browser]
N --> P[Complete Render]
O --> Q[Resume Later]
Q --> M
H --> R[Commit Changes]
P --> R
style B fill:#61dafb
style M fill:#ff6b6b
style R fill:#4ecdc4
Priority Management Implementation:
// Phase 7.1: Priority Calculation
export function requestUpdateLane(fiber) {
const mode = fiber.mode;
if ((mode & ConcurrentMode) === NoMode) {
return SyncLane;
}
// Check for transition context
const isTransition = requestCurrentTransition() !== NoTransition;
if (isTransition) {
if (currentEventTransitionLane === NoLane) {
currentEventTransitionLane = claimNextTransitionLane();
}
return currentEventTransitionLane;
}
// Get priority from current update priority
const updatePriority = getCurrentUpdatePriority();
if (updatePriority !== NoEventPriority) {
return lanePriorityToLanes(updatePriority);
}
// Get priority from scheduler
const schedulerPriority = getCurrentSchedulerPriorityLevel();
const lane = schedulerPriorityToLane(schedulerPriority);
return lane;
}
// Phase 7.2: Concurrent Work Scheduling
function ensureRootIsScheduled(root, currentTime) {
const existingCallbackNode = root.callbackNode;
// Step 1: Mark starved lanes as expired
markStarvedLanesAsExpired(root, currentTime);
// Step 2: Determine next lanes to work on
const nextLanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes);
if (nextLanes === NoLanes) {
// No work to do
if (existingCallbackNode !== null) {
cancelCallback(existingCallbackNode);
}
root.callbackNode = null;
root.callbackPriority = NoLane;
return;
}
// Step 3: Check if we can reuse existing callback
const newCallbackPriority = getHighestPriorityLane(nextLanes);
const existingCallbackPriority = root.callbackPriority;
if (existingCallbackPriority === newCallbackPriority && existingCallbackNode !== null) {
return; // Reuse existing callback
}
// Step 4: Cancel existing callback and schedule new one
if (existingCallbackNode != null) {
cancelCallback(existingCallbackNode);
}
let newCallbackNode;
if (newCallbackPriority === SyncLane) {
// Synchronous work
if (root.tag === LegacyRoot) {
scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));
} else {
scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
}
newCallbackNode = null;
} else {
// Concurrent work
let schedulerPriorityLevel;
switch (lanesToEventPriority(nextLanes)) {
case DiscreteEventPriority:
schedulerPriorityLevel = ImmediateSchedulerPriority;
break;
case ContinuousEventPriority:
schedulerPriorityLevel = UserBlockingSchedulerPriority;
break;
case DefaultEventPriority:
schedulerPriorityLevel = NormalSchedulerPriority;
break;
case IdleEventPriority:
schedulerPriorityLevel = IdleSchedulerPriority;
break;
default:
schedulerPriorityLevel = NormalSchedulerPriority;
break;
}
newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root),
);
}
root.callbackPriority = newCallbackPriority;
root.callbackNode = newCallbackNode;
}flowchart TD
A[Error Occurs] --> B{Error Type?}
B -->|JavaScript Error| C[Component Error]
B -->|Promise Rejection| D[Async Error]
B -->|Render Error| E[Render Phase Error]
C --> F[Find Error Boundary]
D --> G[Find Suspense Boundary]
E --> H[Unwind Work Stack]
F --> I{Error Boundary Found?}
G --> J{Suspense Boundary Found?}
H --> K[Mark Fiber as Incomplete]
I -->|Yes| L[Capture Error]
I -->|No| M[Propagate to Root]
J -->|Yes| N[Suspend Component]
J -->|No| O[Throw to Parent]
K --> P[Continue Unwinding]
L --> Q[Call getDerivedStateFromError]
M --> R[Global Error Handler]
N --> S[Show Fallback UI]
O --> F
P --> T{More Work?}
Q --> U[Schedule Re-render]
S --> V[Wait for Promise]
T -->|Yes| P
T -->|No| W[Complete Error Handling]
U --> X[Render Error UI]
V --> Y[Resume Render]
style B fill:#ff6b6b
style L fill:#4ecdc4
style N fill:#45b7d1
Error Handling Implementation:
// Phase 8.1: Error Capture and Processing
function throwException(root, returnFiber, sourceFiber, value, rootRenderLanes) {
// Mark source fiber as incomplete
sourceFiber.flags |= Incomplete;
if (value !== null && typeof value === 'object' && typeof value.then === 'function') {
// This is a thenable (Promise)
const wakeable = value;
// Find nearest Suspense boundary
let workInProgress = returnFiber;
do {
if (workInProgress.tag === SuspenseComponent && shouldCaptureSuspense(workInProgress)) {
// Attach wakeable to suspense boundary
const wakeables = workInProgress.updateQueue;
if (wakeables === null) {
const updateQueue = new Set();
updateQueue.add(wakeable);
workInProgress.updateQueue = updateQueue;
} else {
wakeables.add(wakeable);
}
// Attach ping listener
attachPingListener(root, wakeable, rootRenderLanes);
workInProgress.flags |= ShouldCapture;
workInProgress.lanes = rootRenderLanes;
return;
}
workInProgress = workInProgress.return;
} while (workInProgress !== null);
// No Suspense boundary found, treat as error
value = new Error('A component suspended while responding to synchronous input.');
}
// Handle regular errors
let workInProgress = returnFiber;
do {
switch (workInProgress.tag) {
case HostRoot: {
const errorInfo = value;
workInProgress.flags |= ShouldCapture;
const lane = pickArbitraryLane(rootRenderLanes);
workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);
const update = createRootErrorUpdate(workInProgress, errorInfo, lane);
enqueueCapturedUpdate(workInProgress, update);
return;
}
case ClassComponent: {
const errorInfo = value;
const ctor = workInProgress.type;
const instance = workInProgress.stateNode;
if (
(workInProgress.flags & DidCapture) === NoFlags &&
(typeof ctor.getDerivedStateFromError === 'function' ||
(instance !== null && typeof instance.componentDidCatch === 'function'))
) {
workInProgress.flags |= ShouldCapture;
const lane = pickArbitraryLane(rootRenderLanes);
workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);
const update = createClassErrorUpdate(workInProgress, errorInfo, lane);
enqueueCapturedUpdate(workInProgress, update);
return;
}
break;
}
}
workInProgress = workInProgress.return;
} while (workInProgress !== null);
}Step 1: React Package Loading
// src/react/src/React.js
import { useReducer, useState, useEffect } from "./ReactHooks";
import ReactSharedInternals from "./ReactSharedInternals";
export {
useReducer,
useState,
useEffect,
ReactSharedInternals as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
};Step 2: Root Creation Process
// src/react-dom/src/client/ReactDOMRoot.js
export function createRoot(container) {
// 1. Create Fiber root structure
const root = createContainer(container);
// 2. Setup event delegation
listenToAllSupportedEvents(container);
// 3. Return ReactDOMRoot instance
return new ReactDOMRoot(root);
}
function ReactDOMRoot(internalRoot) {
this._internalRoot = internalRoot;
}
ReactDOMRoot.prototype.render = function (children) {
const root = this._internalRoot;
root.containerInfo.innerHTML = "";
updateContainer(children, root);
};Step 3: Fiber Root Structure Creation
// src/react-reconciler/src/ReactFiberRoot.js
export function createFiberRoot(containerInfo) {
// Create FiberRootNode
const root = new FiberRootNode(containerInfo);
// Create HostRoot Fiber
const uninitializedFiber = createHostRootFiber();
// Establish bidirectional connection
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
// Initialize update queue
initialUpdateQueue(uninitializedFiber);
return root;
}The render phase builds the new Fiber tree and can be interrupted:
graph TD
A[updateContainer] --> B[scheduleUpdateOnFiber]
B --> C[ensureRootIsScheduled]
C --> D[scheduleCallback]
D --> E[performConcurrentWorkOnRoot]
E --> F[renderRootConcurrent]
F --> G[workLoopConcurrent]
G --> H[performUnitOfWork]
H --> I[beginWork]
I --> J[completeWork]
J --> K{More work?}
K -->|Yes| H
K -->|No| L[finishConcurrentRender]
// src/react-reconciler/src/ReactFiberWorkLoop.js
function workLoopConcurrent() {
// Work until scheduler tells us to yield
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
function performUnitOfWork(unitOfWork) {
const current = unitOfWork.alternate;
// Begin work phase
let next = beginWork(current, unitOfWork, renderLanes);
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) {
// No child, complete this unit
completeUnitOfWork(unitOfWork);
} else {
// Continue with child
workInProgress = next;
}
}The commit phase applies changes to the DOM synchronously:
function commitRootImpl(root, renderPriorityLevel) {
// Phase 1: Before mutation effects
commitBeforeMutationEffects(root, finishedWork);
// Phase 2: Mutation effects (DOM changes)
commitMutationEffects(root, finishedWork, lanes);
// Switch fiber trees
root.current = finishedWork;
// Phase 3: Layout effects
commitLayoutEffects(finishedWork, root, lanes);
// Schedule passive effects (useEffect)
if (rootDoesHavePassiveEffects) {
scheduleCallback(NormalSchedulerPriority, flushPassiveEffects);
}
}// src/react-reconciler/src/ReactFiber.js
function FiberNode(tag, pendingProps, key, mode) {
// Instance properties
this.tag = tag; // Fiber type
this.key = key; // Unique identifier
this.elementType = null; // Element type
this.type = null; // Function/class reference
this.stateNode = null; // DOM node or instance
// Fiber relationships
this.return = null; // Parent fiber
this.child = null; // First child
this.sibling = null; // Next sibling
this.index = 0; // Position in parent
// Props and state
this.pendingProps = pendingProps; // New props
this.memoizedProps = null; // Current props
this.updateQueue = null; // Update queue
this.memoizedState = null; // Current state
// Effects
this.flags = NoFlags; // Side effects
this.subtreeFlags = NoFlags; // Child effects
this.deletions = null; // Deleted children
// Scheduling
this.lanes = NoLanes; // Priority lanes
this.childLanes = NoLanes; // Child priority
// Double buffering
this.alternate = null; // Alternate fiber
}function completeUnitOfWork(unitOfWork) {
let completedWork = unitOfWork;
do {
const current = completedWork.alternate;
const returnFiber = completedWork.return;
// Complete the work
const next = completeWork(current, completedWork, renderLanes);
if (next !== null) {
// Suspend or error case
workInProgress = next;
return;
}
// Collect side effects
if (returnFiber !== null && (returnFiber.flags & Incomplete) === NoFlags) {
// Append all the effects of the subtree
if (returnFiber.subtreeFlags === NoFlags) {
returnFiber.subtreeFlags = completedWork.subtreeFlags;
} else {
returnFiber.subtreeFlags |= completedWork.subtreeFlags;
}
returnFiber.subtreeFlags |= completedWork.flags;
}
const siblingFiber = completedWork.sibling;
if (siblingFiber !== null) {
// Continue with sibling
workInProgress = siblingFiber;
return;
}
// Move to parent
completedWork = returnFiber;
workInProgress = completedWork;
} while (completedWork !== null);
// We've reached the root
if (workInProgressRootExitStatus === RootInProgress) {
workInProgressRootExitStatus = RootCompleted;
}
}React Hooks use a sophisticated dispatcher pattern that switches implementations based on the rendering phase:
// src/react-reconciler/src/ReactFiberHooks.js
// Mount phase dispatcher
const HooksDispatcherOnMount = {
useReducer: mountReducer,
useState: mountState,
useEffect: mountEffect,
useLayoutEffect: mountLayoutEffect,
useRef: mountRef,
useCallback: mountCallback,
useMemo: mountMemo,
};
// Update phase dispatcher
const HooksDispatcherOnUpdate = {
useReducer: updateReducer,
useState: updateState,
useEffect: updateEffect,
useLayoutEffect: updateLayoutEffect,
useRef: updateRef,
useCallback: updateCallback,
useMemo: updateMemo,
};export function renderWithHooks(
current,
workInProgress,
Component,
props,
renderLanes
) {
// Set current rendering fiber
currentlyRenderingFiber = workInProgress;
// Clear hooks list and effects
workInProgress.memoizedState = null;
workInProgress.updateQueue = null;
// Choose dispatcher based on mount/update
if (current !== null && current.memoizedState !== null) {
ReactCurrentDispatcher.current = HooksDispatcherOnUpdate;
} else {
ReactCurrentDispatcher.current = HooksDispatcherOnMount;
}
// Execute component function
const children = Component(props);
// Reset global state
currentlyRenderingFiber = null;
workInProgressHook = null;
currentHook = null;
return children;
}function mountState(initialState) {
// Step 1: Create hook object
const hook = mountWorkInProgressHook();
// Step 2: Initialize state
if (typeof initialState === "function") {
initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState;
// Step 3: Create update queue
const queue = {
pending: null,
interleaved: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: baseStateReducer,
lastRenderedState: initialState,
};
hook.queue = queue;
// Step 4: Create dispatch function
const dispatch = (queue.dispatch = dispatchSetState.bind(
null,
currentlyRenderingFiber,
queue
));
return [hook.memoizedState, dispatch];
}
function mountWorkInProgressHook() {
const hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null,
};
if (workInProgressHook === null) {
// First hook in the component
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
// Append to the end of the list
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}function updateState(initialState) {
return updateReducer(baseStateReducer, initialState);
}
function updateReducer(reducer, initialArg, init) {
const hook = updateWorkInProgressHook();
const queue = hook.queue;
queue.lastRenderedReducer = reducer;
const current = currentHook;
let baseQueue = current.baseQueue;
// Process pending updates
const pendingQueue = queue.pending;
if (pendingQueue !== null) {
if (baseQueue !== null) {
// Merge pending and base queues
const baseFirst = baseQueue.next;
const pendingFirst = pendingQueue.next;
baseQueue.next = pendingFirst;
pendingQueue.next = baseFirst;
}
current.baseQueue = baseQueue = pendingQueue;
queue.pending = null;
}
if (baseQueue !== null) {
// Process the update queue
const first = baseQueue.next;
let newState = current.baseState;
let newBaseState = null;
let newBaseQueueFirst = null;
let newBaseQueueLast = null;
let update = first;
do {
const updateLane = update.lane;
if (!isSubsetOfLanes(renderLanes, updateLane)) {
// Skip this update - insufficient priority
const clone = {
lane: updateLane,
action: update.action,
hasEagerState: update.hasEagerState,
eagerState: update.eagerState,
next: null,
};
if (newBaseQueueLast === null) {
newBaseQueueFirst = newBaseQueueLast = clone;
newBaseState = newState;
} else {
newBaseQueueLast = newBaseQueueLast.next = clone;
}
currentlyRenderingFiber.lanes = mergeLanes(
currentlyRenderingFiber.lanes,
updateLane
);
} else {
// Process this update
if (newBaseQueueLast !== null) {
const clone = {
lane: NoLane,
action: update.action,
hasEagerState: update.hasEagerState,
eagerState: update.eagerState,
next: null,
};
newBaseQueueLast = newBaseQueueLast.next = clone;
}
if (update.hasEagerState) {
newState = update.eagerState;
} else {
const action = update.action;
newState = reducer(newState, action);
}
}
update = update.next;
} while (update !== null && update !== first);
if (newBaseQueueLast === null) {
newBaseState = newState;
} else {
newBaseQueueLast.next = newBaseQueueFirst;
}
if (!Object.is(newState, hook.memoizedState)) {
markWorkInProgressReceivedUpdate();
}
hook.memoizedState = newState;
hook.baseState = newBaseState;
hook.baseQueue = newBaseQueueLast;
queue.lastRenderedState = newState;
}
const dispatch = queue.dispatch;
return [hook.memoizedState, dispatch];
}function dispatchSetState(fiber, queue, action) {
const lane = requestUpdateLane(fiber);
const update = {
lane,
action,
hasEagerState: false,
eagerState: null,
next: null,
};
// Eager state optimization
if (
fiber.lanes === NoLanes &&
(fiber.alternate === null || fiber.alternate.lanes === NoLanes)
) {
const lastRenderedReducer = queue.lastRenderedReducer;
if (lastRenderedReducer !== null) {
try {
const currentState = queue.lastRenderedState;
const eagerState = lastRenderedReducer(currentState, action);
update.hasEagerState = true;
update.eagerState = eagerState;
if (Object.is(eagerState, currentState)) {
// Fast path - no re-render needed
return;
}
} catch (error) {
// Suppress error, will be caught during render
}
}
}
// Enqueue update
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
if (root !== null) {
const eventTime = requestEventTime();
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
}
}function mountEffect(create, deps) {
return mountEffectImpl(
PassiveEffect | PassiveStaticEffect,
HookPassive,
create,
deps
);
}
function mountEffectImpl(fiberFlags, hookFlags, create, deps) {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
currentlyRenderingFiber.flags |= fiberFlags;
hook.memoizedState = pushEffect(
HookHasEffect | hookFlags,
create,
undefined,
nextDeps
);
}
function pushEffect(tag, create, destroy, deps) {
const effect = {
tag,
create,
destroy,
deps,
next: null,
};
let componentUpdateQueue = currentlyRenderingFiber.updateQueue;
if (componentUpdateQueue === null) {
componentUpdateQueue = createFunctionComponentUpdateQueue();
currentlyRenderingFiber.updateQueue = componentUpdateQueue;
componentUpdateQueue.lastEffect = effect.next = effect;
} else {
const lastEffect = componentUpdateQueue.lastEffect;
if (lastEffect === null) {
componentUpdateQueue.lastEffect = effect.next = effect;
} else {
const firstEffect = lastEffect.next;
lastEffect.next = effect;
effect.next = firstEffect;
componentUpdateQueue.lastEffect = effect;
}
}
return effect;
}function updateEffect(create, deps) {
return updateEffectImpl(PassiveEffect, HookPassive, create, deps);
}
function updateEffectImpl(fiberFlags, hookFlags, create, deps) {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
let destroy = undefined;
if (currentHook !== null) {
const prevEffect = currentHook.memoizedState;
destroy = prevEffect.destroy;
if (nextDeps !== null) {
const prevDeps = prevEffect.deps;
if (areHookInputsEqual(nextDeps, prevDeps)) {
// Dependencies haven't changed, skip effect
hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps);
return;
}
}
}
currentlyRenderingFiber.flags |= fiberFlags;
hook.memoizedState = pushEffect(
HookHasEffect | hookFlags,
create,
destroy,
nextDeps
);
}
function areHookInputsEqual(nextDeps, prevDeps) {
if (prevDeps === null) {
return false;
}
for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
if (Object.is(nextDeps[i], prevDeps[i])) {
continue;
}
return false;
}
return true;
}function mountCallback(callback, deps) {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
hook.memoizedState = [callback, nextDeps];
return callback;
}
function updateCallback(callback, deps) {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
if (nextDeps !== null) {
const prevDeps = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
}
hook.memoizedState = [callback, nextDeps];
return callback;
}function mountMemo(nextCreate, deps) {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
function updateMemo(nextCreate, deps) {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
if (nextDeps !== null) {
const prevDeps = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
}
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}The DOM Diff algorithm is the heart of React's reconciliation process, responsible for efficiently updating the DOM by comparing the current Fiber tree with the new one. React's diff algorithm operates on the principle of minimizing DOM operations while maintaining optimal performance.
graph TB
A[New Element Tree] --> B[Reconciliation Process]
C[Current Fiber Tree] --> B
B --> D[DOM Diff Algorithm]
D --> E[Minimal DOM Updates]
E --> F[Updated DOM]
style D fill:#ff6b6b
style E fill:#4ecdc4
Key Assumptions:
- Same Level Comparison: Elements are only compared at the same tree level
- Type-Based Optimization: Different element types result in complete subtree replacement
- Key-Based Identification: Keys help identify moved elements efficiently
- Heuristic Approach: Uses heuristics rather than optimal solutions for performance
// src/react-reconciler/src/ReactChildFiber.js
function reconcileChildFibers(
returnFiber,
currentFirstChild,
newChild,
lanes
) {
// Phase 1: Handle different child types
if (typeof newChild === 'object' && newChild !== null) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE:
return placeSingleChild(
reconcileSingleElement(returnFiber, currentFirstChild, newChild, lanes)
);
case REACT_FRAGMENT_TYPE:
return reconcileSingleFragment(returnFiber, currentFirstChild, newChild, lanes);
}
// Phase 2: Handle arrays (multiple children)
if (Array.isArray(newChild)) {
return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, lanes);
}
}
// Phase 3: Handle text content
if (typeof newChild === 'string' || typeof newChild === 'number') {
return placeSingleChild(
reconcileSingleTextNode(returnFiber, currentFirstChild, '' + newChild, lanes)
);
}
// Phase 4: Delete remaining children
return deleteRemainingChildren(returnFiber, currentFirstChild);
}When elements have the same type, React reuses the existing DOM node and updates its properties:
flowchart TD
A[Current: div key='a'] --> B{Same Type?}
C[New: div key='a'] --> B
B -->|Yes| D[Reuse Fiber Node]
D --> E[Update Props]
E --> F[Reconcile Children]
F --> G[Mark for Update]
style D fill:#4ecdc4
style E fill:#45b7d1
function reconcileSingleElement(returnFiber, currentFirstChild, element, lanes) {
const key = element.key;
let child = currentFirstChild;
// Search for existing child with same key
while (child !== null) {
if (child.key === key) {
const elementType = element.type;
if (child.elementType === elementType) {
// Same type - reuse existing fiber
deleteRemainingChildren(returnFiber, child.sibling);
const existing = useFiber(child, element.props);
existing.ref = coerceRef(returnFiber, child, element);
existing.return = returnFiber;
return existing;
} else {
// Different type - delete all and create new
deleteRemainingChildren(returnFiber, child);
break;
}
} else {
// Different key - delete this child
deleteChild(returnFiber, child);
}
child = child.sibling;
}
// Create new fiber
const created = createFiberFromElement(element, returnFiber.mode, lanes);
created.ref = coerceRef(returnFiber, currentFirstChild, element);
created.return = returnFiber;
return created;
}When element types differ, React replaces the entire subtree:
flowchart TD
A[Current: div] --> B{Same Type?}
C[New: span] --> B
B -->|No| D[Delete Old Subtree]
D --> E[Create New Fiber]
E --> F[Mount New Subtree]
F --> G[Mark for Placement]
style D fill:#ff6b6b
style E fill:#4ecdc4
The most complex part of the diff algorithm handles arrays of children:
flowchart TD
A[Start Array Reconciliation] --> B[Phase 1: Handle Common Prefix]
B --> C[Phase 2: Handle Common Suffix]
C --> D[Phase 3: Handle Remaining Insertions/Deletions]
D --> E[Phase 4: Handle Moves with Key Map]
E --> F[Finish Reconciliation]
style B fill:#61dafb
style C fill:#4ecdc4
style D fill:#ff6b6b
style E fill:#45b7d1
function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren, lanes) {
let resultingFirstChild = null;
let previousNewFiber = null;
let oldFiber = currentFirstChild;
let lastPlacedIndex = 0;
let newIdx = 0;
let nextOldFiber = null;
// Phase 1: Handle updates for common prefix
for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
if (oldFiber.index > newIdx) {
nextOldFiber = oldFiber;
oldFiber = null;
} else {
nextOldFiber = oldFiber.sibling;
}
const newFiber = updateSlot(returnFiber, oldFiber, newChildren[newIdx], lanes);
if (newFiber === null) {
if (oldFiber === null) {
oldFiber = nextOldFiber;
}
break; // No match found
}
if (shouldTrackSideEffects) {
if (oldFiber && newFiber.alternate === null) {
deleteChild(returnFiber, oldFiber);
}
}
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
resultingFirstChild = newFiber;
} else {
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
oldFiber = nextOldFiber;
}
// Phase 2: Handle remaining new children (insertions)
if (newIdx === newChildren.length) {
deleteRemainingChildren(returnFiber, oldFiber);
return resultingFirstChild;
}
// Phase 3: Handle remaining old children (deletions)
if (oldFiber === null) {
for (; newIdx < newChildren.length; newIdx++) {
const newFiber = createChild(returnFiber, newChildren[newIdx], lanes);
if (newFiber === null) continue;
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
resultingFirstChild = newFiber;
} else {
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
}
return resultingFirstChild;
}
// Phase 4: Handle moves using key map
const existingChildren = mapRemainingChildren(returnFiber, oldFiber);
for (; newIdx < newChildren.length; newIdx++) {
const newFiber = updateFromMap(
existingChildren,
returnFiber,
newIdx,
newChildren[newIdx],
lanes
);
if (newFiber !== null) {
if (shouldTrackSideEffects) {
if (newFiber.alternate !== null) {
existingChildren.delete(
newFiber.key === null ? newIdx : newFiber.key
);
}
}
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
resultingFirstChild = newFiber;
} else {
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
}
}
// Delete remaining old children
if (shouldTrackSideEffects) {
existingChildren.forEach(child => deleteChild(returnFiber, child));
}
return resultingFirstChild;
}Before:
<div>
<span>Hello</span>
<p>World</p>
</div>After:
<div>
<span>Hi</span>
<p>World</p>
</div>flowchart LR
A[div] --> B[span: 'Hello']
A --> C[p: 'World']
D[div] --> E[span: 'Hi']
D --> F[p: 'World']
B -.->|Update Text| E
C -.->|No Change| F
style E fill:#4ecdc4
style F fill:#90EE90
Diff Process:
- Compare
divelements - same type, reuse - Compare
spanchildren - same type, update text content - Compare
pchildren - same type and content, no change needed
Before:
<div>
<span>Text</span>
</div>After:
<div>
<p>Text</p>
</div>flowchart LR
A[div] --> B[span: 'Text']
C[div] --> D[p: 'Text']
B -.->|Delete| X[β]
X -.->|Create| D
style B fill:#ff6b6b
style D fill:#4ecdc4
Diff Process:
- Compare
divelements - same type, reuse - Compare children:
spanvsp- different types - Delete
spanfiber and its subtree - Create new
pfiber with same content
Before:
<ul>
<li key="a">Item A</li>
<li key="b">Item B</li>
<li key="c">Item C</li>
</ul>After:
<ul>
<li key="c">Item C</li>
<li key="a">Item A</li>
<li key="b">Item B</li>
</ul>flowchart TD
subgraph "Before"
A1[li key='a'] --> A2[li key='b'] --> A3[li key='c']
end
subgraph "After"
B1[li key='c'] --> B2[li key='a'] --> B3[li key='b']
end
subgraph "Key Map Phase"
C1[Map: aβfiber_a, bβfiber_b, cβfiber_c]
end
A3 -.->|Move to position 0| B1
A1 -.->|Move to position 1| B2
A2 -.->|Move to position 2| B3
style B1 fill:#45b7d1
style B2 fill:#45b7d1
style B3 fill:#45b7d1
Diff Process:
- Phase 1: No common prefix found (c β a)
- Phase 2: Create key map:
{a: fiber_a, b: fiber_b, c: fiber_c} - Phase 3: For each new position:
- Position 0: Find key 'c' in map, move fiber_c
- Position 1: Find key 'a' in map, move fiber_a
- Position 2: Find key 'b' in map, move fiber_b
Before:
<ul>
<li key="a">Item A</li>
<li key="b">Item B</li>
<li key="d">Item D</li>
</ul>After:
<ul>
<li key="a">Item A</li>
<li key="c">Item C</li>
<li key="d">Item D</li>
<li key="e">Item E</li>
</ul>flowchart TD
subgraph "Operations"
A[Keep: li key='a']
B[Delete: li key='b']
C[Insert: li key='c']
D[Keep: li key='d']
E[Insert: li key='e']
end
A --> C --> D --> E
B -.->|β Delete| X[Deleted]
style A fill:#90EE90
style B fill:#ff6b6b
style C fill:#4ecdc4
style D fill:#90EE90
style E fill:#4ecdc4
Diff Process:
- Phase 1: Compare 'a' with 'a' - match, keep
- Phase 1: Compare 'b' with 'c' - no match, break to key map phase
- Key map phase:
- Create map:
{b: fiber_b, d: fiber_d} - Position 1: key 'c' not in map - create new fiber
- Position 2: key 'd' in map - reuse fiber_d
- Position 3: key 'e' not in map - create new fiber
- Create map:
- Delete remaining: fiber_b marked for deletion
Before:
<div>
<span>A</span>
<span>B</span>
</div>After:
<div>
<React.Fragment>
<span>A</span>
<p>C</p>
</React.Fragment>
<span>B</span>
</div>flowchart TD
subgraph "Before"
A[div] --> B[span: A]
A --> C[span: B]
end
subgraph "After"
D[div] --> E[Fragment]
E --> F[span: A]
E --> G[p: C]
D --> H[span: B]
end
B -.->|Reuse| F
C -.->|Reuse| H
G -.->|Create| G
style F fill:#90EE90
style G fill:#4ecdc4
style H fill:#90EE90
function placeChild(newFiber, lastPlacedIndex, newIndex) {
newFiber.index = newIndex;
if (!shouldTrackSideEffects) {
return lastPlacedIndex;
}
const current = newFiber.alternate;
if (current !== null) {
const oldIndex = current.index;
if (oldIndex < lastPlacedIndex) {
// This is a move
newFiber.flags |= Placement;
return lastPlacedIndex;
} else {
// This item can stay in place
return oldIndex;
}
} else {
// This is an insertion
newFiber.flags |= Placement;
return lastPlacedIndex;
}
}function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) {
if (current !== null) {
workInProgress.dependencies = current.dependencies;
}
// Check if children have pending work
if (!includesSomeLane(renderLanes, workInProgress.childLanes)) {
// No pending work in subtree, can skip entirely
return null;
}
// Clone child fibers
cloneChildFibers(current, workInProgress);
return workInProgress.child;
}function reconcileChildFibers(returnFiber, currentFirstChild, newChild, lanes) {
// Handle null/undefined
if (newChild == null) {
return deleteRemainingChildren(returnFiber, currentFirstChild);
}
// Handle boolean values (render nothing)
if (typeof newChild === 'boolean') {
return deleteRemainingChildren(returnFiber, currentFirstChild);
}
// Continue with normal reconciliation...
}function reconcilePortal(returnFiber, currentFirstChild, portal, lanes) {
const key = portal.key;
let child = currentFirstChild;
while (child !== null) {
if (child.key === key && child.tag === HostPortal) {
if (child.stateNode.containerInfo === portal.containerInfo) {
// Same portal, update children
deleteRemainingChildren(returnFiber, child.sibling);
const existing = useFiber(child, portal.children || []);
existing.return = returnFiber;
return existing;
} else {
// Different container, replace
deleteRemainingChildren(returnFiber, child);
break;
}
} else {
deleteChild(returnFiber, child);
}
child = child.sibling;
}
const created = createFiberFromPortal(portal, returnFiber.mode, lanes);
created.return = returnFiber;
return created;
}| Scenario | Best Case | Average Case | Worst Case |
|---|---|---|---|
| Single Element | O(1) | O(1) | O(n) |
| Array - No Keys | O(n) | O(nΒ²) | O(nΒ²) |
| Array - With Keys | O(n) | O(n) | O(nΒ²) |
| Deep Tree | O(n) | O(n) | O(n) |
// Key map creation for large lists
function mapRemainingChildren(returnFiber, currentFirstChild) {
const existingChildren = new Map();
let existingChild = currentFirstChild;
while (existingChild !== null) {
if (existingChild.key !== null) {
existingChildren.set(existingChild.key, existingChild);
} else {
existingChildren.set(existingChild.index, existingChild);
}
existingChild = existingChild.sibling;
}
return existingChildren; // O(n) space for n children
}// β
Good: Stable, unique keys
const items = data.map(item => (
<Item key={item.id} data={item} />
));
// β Bad: Array indices as keys (breaks reordering optimization)
const items = data.map((item, index) => (
<Item key={index} data={item} />
));
// β Bad: Non-unique keys
const items = data.map(item => (
<Item key={item.category} data={item} />
));// β
Good: Stable component structure
function OptimizedList({ items, filter }) {
const filteredItems = useMemo(() =>
items.filter(item => item.category === filter),
[items, filter]
);
return (
<ul>
{filteredItems.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
// β Bad: Conditional rendering changes structure
function UnoptimizedList({ items, showHeader }) {
return (
<div>
{showHeader && <h2>Items</h2>}
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
}React 18 uses a sophisticated binary lane system for managing update priorities:
// src/react-reconciler/src/ReactFiberLane.js
// Lane definitions (binary flags)
export const NoLanes = 0b0000000000000000000000000000000;
export const NoLane = NoLanes;
export const SyncLane = 0b0000000000000000000000000000001;
export const InputContinuousHydrationLane = 0b0000000000000000000000000000010;
export const InputContinuousLane = 0b0000000000000000000000000000100;
export const DefaultHydrationLane = 0b0000000000000000000000000001000;
export const DefaultLane = 0b0000000000000000000000000010000;
export const TransitionHydrationLane = 0b0000000000000000000000000100000;
export const TransitionLanes = 0b0000000001111111111111111000000;
export const RetryLanes = 0b0000011110000000000000000000000;
export const SomeRetryLane = 0b0000010000000000000000000000000;
export const SelectiveHydrationLane = 0b0000100000000000000000000000000;
export const IdleHydrationLane = 0b0001000000000000000000000000000;
export const IdleLane = 0b0010000000000000000000000000000;
export const OffscreenLane = 0b0100000000000000000000000000000;export function mergeLanes(a, b) {
return a | b;
}
export function removeLanes(set, subset) {
return set & ~subset;
}
export function intersectLanes(a, b) {
return a & b;
}
export function isSubsetOfLanes(set, subset) {
return (set & subset) === subset;
}
export function getHighestPriorityLane(lanes) {
return lanes & -lanes; // Isolate rightmost set bit
}
export function getNextLanes(root, wipLanes) {
const pendingLanes = root.pendingLanes;
if (pendingLanes === NoLanes) {
return NoLanes;
}
let nextLanes = NoLanes;
const expiredLanes = root.expiredLanes;
const suspendedLanes = root.suspendedLanes;
const pingedLanes = root.pingedLanes;
// Check for expired lanes first
if (expiredLanes !== NoLanes) {
nextLanes = mergeLanes(nextLanes, expiredLanes);
}
// Process non-idle lanes
const nonIdlePendingLanes = pendingLanes & NonIdleLanes;
if (nonIdlePendingLanes !== NoLanes) {
const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes;
if (nonIdleUnblockedLanes !== NoLanes) {
nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes);
} else {
const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes;
if (nonIdlePingedLanes !== NoLanes) {
nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);
}
}
}
return nextLanes;
}function updateSuspenseComponent(current, workInProgress, renderLanes) {
const nextProps = workInProgress.pendingProps;
let suspenseContext = suspenseStackCursor.current;
let showFallback = false;
const didSuspend = (workInProgress.flags & DidCapture) !== NoFlags;
if (didSuspend || shouldRemainOnFallback(suspenseContext, current)) {
showFallback = true;
workInProgress.flags &= ~DidCapture;
}
suspenseContext = setDefaultShallowSuspenseContext(suspenseContext);
pushSuspenseContext(workInProgress, suspenseContext);
if (current === null) {
// Mount
return mountSuspenseComponent(
workInProgress,
nextProps,
showFallback,
renderLanes
);
} else {
// Update
return updateSuspenseComponentImpl(
current,
workInProgress,
nextProps,
showFallback,
renderLanes
);
}
}function throwException(
root,
returnFiber,
sourceFiber,
value,
rootRenderLanes
) {
sourceFiber.flags |= Incomplete;
if (
value !== null &&
typeof value === "object" &&
typeof value.then === "function"
) {
// This is a promise (thenable)
const wakeable = value;
// Find the nearest Suspense boundary
let workInProgress = returnFiber;
do {
if (
workInProgress.tag === SuspenseComponent &&
shouldCaptureSuspense(workInProgress, hasInvisibleParentBoundary)
) {
// Found boundary - attach wakeable
const wakeables = workInProgress.updateQueue;
if (wakeables === null) {
const updateQueue = new Set();
updateQueue.add(wakeable);
workInProgress.updateQueue = updateQueue;
} else {
wakeables.add(wakeable);
}
// Attach ping listener
attachPingListener(root, wakeable, rootRenderLanes);
workInProgress.flags |= ShouldCapture;
workInProgress.lanes = rootRenderLanes;
return;
}
workInProgress = workInProgress.return;
} while (workInProgress !== null);
}
}
function attachPingListener(root, wakeable, lanes) {
let pingCache = root.pingCache;
let threadIDs;
if (pingCache === null) {
pingCache = root.pingCache = new WeakMap();
threadIDs = new Set();
pingCache.set(wakeable, threadIDs);
} else {
threadIDs = pingCache.get(wakeable);
if (threadIDs === undefined) {
threadIDs = new Set();
pingCache.set(wakeable, threadIDs);
}
}
if (!threadIDs.has(lanes)) {
threadIDs.add(lanes);
const ping = pingSuspendedRoot.bind(null, root, wakeable, lanes);
wakeable.then(ping, ping);
}
}function startTransition(callback) {
const prevTransition = ReactCurrentBatchConfig.transition;
ReactCurrentBatchConfig.transition = {};
try {
callback();
} finally {
ReactCurrentBatchConfig.transition = prevTransition;
}
}
function useTransition() {
const [isPending, setPending] = useState(false);
const startTransition = useCallback((callback) => {
setPending(true);
const prevTransition = ReactCurrentBatchConfig.transition;
ReactCurrentBatchConfig.transition = {};
try {
setPending(false);
callback();
} finally {
ReactCurrentBatchConfig.transition = prevTransition;
}
}, []);
return [isPending, startTransition];
}export function requestUpdateLane(fiber) {
const mode = fiber.mode;
if ((mode & ConcurrentMode) === NoMode) {
return SyncLane;
}
// Check if we're in a transition
const isTransition = requestCurrentTransition() !== NoTransition;
if (isTransition) {
if (currentEventTransitionLane === NoLane) {
currentEventTransitionLane = claimNextTransitionLane();
}
return currentEventTransitionLane;
}
// Get priority from Scheduler
const schedulerPriority = getCurrentSchedulerPriorityLevel();
let lane;
switch (schedulerPriority) {
case ImmediateSchedulerPriority:
lane = SyncLane;
break;
case UserBlockingSchedulerPriority:
lane = InputContinuousLane;
break;
case NormalSchedulerPriority:
case LowSchedulerPriority:
lane = DefaultLane;
break;
case IdleSchedulerPriority:
lane = IdleLane;
break;
default:
lane = DefaultLane;
break;
}
return lane;
}React uses a single event delegation system at the root container:
// src/react-dom-bindings/src/events/DOMPluginEventSystem.js
export function listenToAllSupportedEvents(rootContainerElement) {
if (!rootContainerElement[listeningMarker]) {
rootContainerElement[listeningMarker] = true;
// Register all native events
allNativeEvents.forEach((domEventName) => {
// Register for both capture and bubble phases
listenToNativeEvent(domEventName, true, rootContainerElement);
listenToNativeEvent(domEventName, false, rootContainerElement);
});
}
}
function listenToNativeEvent(domEventName, isCapturePhaseListener, target) {
let eventSystemFlags = 0;
if (isCapturePhaseListener) {
eventSystemFlags |= IS_CAPTURE_PHASE;
}
addTrappedEventListener(
target,
domEventName,
eventSystemFlags,
isCapturePhaseListener
);
}// Event name mapping
const simpleEventPluginEvents = [
"abort",
"auxClick",
"cancel",
"canPlay",
"canPlayThrough",
"click",
"close",
"contextMenu",
"copy",
"cut",
"drag",
"dragEnd",
"dragEnter",
"dragExit",
"dragLeave",
"dragOver",
"dragStart",
"drop",
"durationChange",
"emptied",
"encrypted",
// ... more events
];
export function registerSimpleEvents() {
for (let i = 0; i < simpleEventPluginEvents.length; i++) {
const eventName = simpleEventPluginEvents[i];
const domEventName = eventName.toLowerCase();
const capitalizedEvent = eventName[0].toUpperCase() + eventName.slice(1);
registerSimpleEvent(domEventName, `on${capitalizedEvent}`);
}
}
function registerSimpleEvent(domEventName, reactName) {
topLevelEventsToReactNames.set(domEventName, reactName);
registerTwoPhaseEvent(reactName, [domEventName]);
}
export function registerTwoPhaseEvent(registrationName, dependencies) {
registerDirectEvent(registrationName, dependencies);
registerDirectEvent(registrationName + "Capture", dependencies);
}function dispatchEventForPlugins(
domEventName,
eventSystemFlags,
nativeEvent,
targetInst,
targetContainer
) {
const nativeEventTarget = getEventTarget(nativeEvent);
const dispatchQueue = [];
// Extract events from the fiber tree
extractEvents(
dispatchQueue,
domEventName,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer
);
// Process the dispatch queue
processDispatchQueue(dispatchQueue, eventSystemFlags);
}
function extractEvents(
dispatchQueue,
domEventName,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer
) {
const reactName = topLevelEventsToReactNames.get(domEventName);
if (reactName === undefined) {
return;
}
let SyntheticEventCtor = SyntheticEvent;
let reactEventType = domEventName;
// Create synthetic event
const event = new SyntheticEventCtor(
reactName,
reactEventType,
null,
nativeEvent,
nativeEventTarget
);
// Collect listeners from fiber tree
const listeners = accumulateSinglePhaseListeners(
targetInst,
reactName,
nativeEvent.type,
(eventSystemFlags & IS_CAPTURE_PHASE) !== 0
);
if (listeners.length > 0) {
dispatchQueue.push({ event, listeners });
}
}// src/react-dom-bindings/src/events/SyntheticEvent.js
function SyntheticEvent(
reactName,
reactEventType,
targetInst,
nativeEvent,
nativeEventTarget
) {
this._reactName = reactName;
this._targetInst = targetInst;
this.type = reactEventType;
this.nativeEvent = nativeEvent;
this.target = nativeEventTarget;
this.currentTarget = null;
// Copy native event properties
for (const propName in Interface) {
if (!Interface.hasOwnProperty(propName)) {
continue;
}
const normalize = Interface[propName];
if (normalize) {
this[propName] = normalize(nativeEvent);
} else {
this[propName] = nativeEvent[propName];
}
}
const defaultPrevented =
nativeEvent.defaultPrevented != null
? nativeEvent.defaultPrevented
: nativeEvent.returnValue === false;
if (defaultPrevented) {
this.isDefaultPrevented = functionThatReturnsTrue;
} else {
this.isDefaultPrevented = functionThatReturnsFalse;
}
this.isPropagationStopped = functionThatReturnsFalse;
return this;
}
Object.assign(SyntheticEvent.prototype, {
preventDefault: function () {
this.defaultPrevented = true;
const event = this.nativeEvent;
if (!event) {
return;
}
if (event.preventDefault) {
event.preventDefault();
} else if (typeof event.returnValue !== "unknown") {
event.returnValue = false;
}
this.isDefaultPrevented = functionThatReturnsTrue;
},
stopPropagation: function () {
const event = this.nativeEvent;
if (!event) {
return;
}
if (event.stopPropagation) {
event.stopPropagation();
} else if (typeof event.cancelBubble !== "unknown") {
event.cancelBubble = true;
}
this.isPropagationStopped = functionThatReturnsTrue;
},
});The React Scheduler manages task execution with priority-based scheduling and time slicing:
// src/scheduler/src/forks/Scheduler.js
// Priority levels
export const ImmediatePriority = 1;
export const UserBlockingPriority = 2;
export const NormalPriority = 3;
export const LowPriority = 4;
export const IdlePriority = 5;
// Timeout values for different priorities
const IMMEDIATE_PRIORITY_TIMEOUT = -1;
const USER_BLOCKING_PRIORITY_TIMEOUT = 250;
const NORMAL_PRIORITY_TIMEOUT = 5000;
const LOW_PRIORITY_TIMEOUT = 10000;
const IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt;
// Task queue (min heap)
const taskQueue = [];
const timerQueue = [];function scheduleCallback(priorityLevel, callback, options) {
const currentTime = getCurrentTime();
let startTime;
if (typeof options === "object" && options !== null) {
const delay = options.delay;
if (typeof delay === "number" && delay > 0) {
startTime = currentTime + delay;
} else {
startTime = currentTime;
}
} else {
startTime = currentTime;
}
let timeout;
switch (priorityLevel) {
case ImmediatePriority:
timeout = IMMEDIATE_PRIORITY_TIMEOUT;
break;
case UserBlockingPriority:
timeout = USER_BLOCKING_PRIORITY_TIMEOUT;
break;
case IdlePriority:
timeout = IDLE_PRIORITY_TIMEOUT;
break;
case LowPriority:
timeout = LOW_PRIORITY_TIMEOUT;
break;
case NormalPriority:
default:
timeout = NORMAL_PRIORITY_TIMEOUT;
break;
}
const expirationTime = startTime + timeout;
const newTask = {
id: taskIdCounter++,
callback,
priorityLevel,
startTime,
expirationTime,
sortIndex: -1,
};
if (startTime > currentTime) {
// This is a delayed task
newTask.sortIndex = startTime;
push(timerQueue, newTask);
if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
if (isHostTimeoutScheduled) {
cancelHostTimeout();
} else {
isHostTimeoutScheduled = true;
}
requestHostTimeout(handleTimeout, startTime - currentTime);
}
} else {
newTask.sortIndex = expirationTime;
push(taskQueue, newTask);
if (!isHostCallbackScheduled && !isPerformingWork) {
isHostCallbackScheduled = true;
requestHostCallback(flushWork);
}
}
return newTask;
}// src/scheduler/src/forks/SchedulerMinHeap.js
export function push(heap, node) {
const index = heap.length;
heap.push(node);
siftUp(heap, node, index);
}
export function peek(heap) {
return heap.length === 0 ? null : heap[0];
}
export function pop(heap) {
if (heap.length === 0) {
return null;
}
const first = heap[0];
const last = heap.pop();
if (last !== first) {
heap[0] = last;
siftDown(heap, last, 0);
}
return first;
}
function siftUp(heap, node, i) {
let index = i;
while (index > 0) {
const parentIndex = (index - 1) >>> 1;
const parent = heap[parentIndex];
if (compare(parent, node) > 0) {
// The parent is larger. Swap positions.
heap[parentIndex] = node;
heap[index] = parent;
index = parentIndex;
} else {
// The parent is smaller. Exit.
return;
}
}
}
function siftDown(heap, node, i) {
let index = i;
const length = heap.length;
const halfLength = length >>> 1;
while (index < halfLength) {
const leftIndex = (index + 1) * 2 - 1;
const left = heap[leftIndex];
const rightIndex = leftIndex + 1;
const right = heap[rightIndex];
// If the left or right node is smaller, swap with the smaller of those.
if (compare(left, node) < 0) {
if (rightIndex < length && compare(right, left) < 0) {
heap[index] = right;
heap[rightIndex] = node;
index = rightIndex;
} else {
heap[index] = left;
heap[leftIndex] = node;
index = leftIndex;
}
} else if (rightIndex < length && compare(right, node) < 0) {
heap[index] = right;
heap[rightIndex] = node;
index = rightIndex;
} else {
// Neither child is smaller. Exit.
return;
}
}
}
function compare(a, b) {
// Compare sort index first, then task id.
const diff = a.sortIndex - b.sortIndex;
return diff !== 0 ? diff : a.id - b.id;
}function workLoop(hasTimeRemaining, initialTime) {
let currentTime = initialTime;
advanceTimers(currentTime);
currentTask = peek(taskQueue);
while (
currentTask !== null &&
!(enableSchedulerDebugging && isSchedulerPaused)
) {
if (
currentTask.expirationTime > currentTime &&
(!hasTimeRemaining || shouldYieldToHost())
) {
// This currentTask hasn't expired, and we've reached the deadline.
break;
}
const callback = currentTask.callback;
if (typeof callback === "function") {
currentTask.callback = null;
currentPriorityLevel = currentTask.priorityLevel;
const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;
const continuationCallback = callback(didUserCallbackTimeout);
currentTime = getCurrentTime();
if (typeof continuationCallback === "function") {
currentTask.callback = continuationCallback;
} else {
if (currentTask === peek(taskQueue)) {
pop(taskQueue);
}
}
advanceTimers(currentTime);
} else {
pop(taskQueue);
}
currentTask = peek(taskQueue);
}
// Return whether there's additional work
if (currentTask !== null) {
return true;
} else {
const firstTimer = peek(timerQueue);
if (firstTimer !== null) {
requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
}
return false;
}
}
function shouldYieldToHost() {
const timeElapsed = getCurrentTime() - startTime;
if (timeElapsed < frameInterval) {
// The main thread has only been blocked for a really short amount of time;
// smaller than a single frame. Don't yield yet.
return false;
}
// The main thread has been blocked for a non-negligible amount of time. We
// may want to yield control of the main thread, so the browser can perform
// high priority tasks. The main ones are painting and user input. If there's
// a pending paint or a pending input, then we should yield. But if there's
// neither, then we can yield less often while remaining responsive. We'll
// eventually yield regardless, since there could be a pending paint that
// wasn't accompanied by a call to `requestPaint`, or other main thread tasks
// like network events.
if (enableIsInputPending) {
if (needsPaint) {
return true;
}
if (timeElapsed < continuousInputInterval) {
if (isInputPending !== null) {
return isInputPending();
}
} else if (timeElapsed < maxInterval) {
if (isInputPending !== null) {
return isInputPending(continuousOptions);
}
} else {
// We've blocked the thread for a long time. Even if there's no pending
// input, there may be some other scheduled work that we don't know about,
// like a network event. Yield now.
return true;
}
}
// `isInputPending` isn't available. Yield now.
return true;
}function updateClassComponent(
current,
workInProgress,
Component,
nextProps,
renderLanes
) {
// ... component update logic
const nextUnitOfWork = finishClassComponent(
current,
workInProgress,
Component,
shouldUpdate,
hasContext,
renderLanes
);
return nextUnitOfWork;
}
function finishClassComponent(
current,
workInProgress,
Component,
shouldUpdate,
hasContext,
renderLanes
) {
markRef(current, workInProgress);
const didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags;
if (!shouldUpdate && !didCaptureError) {
if (hasContext) {
invalidateContextProvider(workInProgress, Component, false);
}
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
const instance = workInProgress.stateNode;
ReactCurrentOwner.current = workInProgress;
let nextChildren;
if (
didCaptureError &&
typeof Component.getDerivedStateFromError !== "function"
) {
nextChildren = null;
} else {
nextChildren = instance.render();
}
workInProgress.flags |= PerformedWork;
if (current !== null && didCaptureError) {
forceUnmountCurrentAndReconcile(
current,
workInProgress,
nextChildren,
renderLanes
);
} else {
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
}
workInProgress.memoizedState = instance.state;
if (hasContext) {
invalidateContextProvider(workInProgress, Component, true);
}
return workInProgress.child;
}function throwException(
root,
returnFiber,
sourceFiber,
value,
rootRenderLanes
) {
sourceFiber.flags |= Incomplete;
if (
value !== null &&
typeof value === "object" &&
typeof value.then === "function"
) {
// This is a thenable (promise)
const wakeable = value;
// Find the nearest Suspense boundary
let workInProgress = returnFiber;
do {
if (workInProgress.tag === SuspenseComponent) {
// Found Suspense boundary
const wakeables = workInProgress.updateQueue;
if (wakeables === null) {
const updateQueue = new Set();
updateQueue.add(wakeable);
workInProgress.updateQueue = updateQueue;
} else {
wakeables.add(wakeable);
}
workInProgress.flags |= ShouldCapture;
workInProgress.lanes = rootRenderLanes;
return;
}
workInProgress = workInProgress.return;
} while (workInProgress !== null);
} else {
// This is an error
let workInProgress = returnFiber;
do {
switch (workInProgress.tag) {
case HostRoot: {
const errorInfo = value;
workInProgress.flags |= ShouldCapture;
const lane = pickArbitraryLane(rootRenderLanes);
workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);
const update = createRootErrorUpdate(workInProgress, errorInfo, lane);
enqueueCapturedUpdate(workInProgress, update);
return;
}
case ClassComponent:
const errorInfo = value;
const ctor = workInProgress.type;
const instance = workInProgress.stateNode;
if (
(workInProgress.flags & DidCapture) === NoFlags &&
(typeof ctor.getDerivedStateFromError === "function" ||
(instance !== null &&
typeof instance.componentDidCatch === "function"))
) {
workInProgress.flags |= ShouldCapture;
const lane = pickArbitraryLane(rootRenderLanes);
workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);
const update = createClassErrorUpdate(
workInProgress,
errorInfo,
lane
);
enqueueCapturedUpdate(workInProgress, update);
return;
}
break;
}
workInProgress = workInProgress.return;
} while (workInProgress !== null);
}
}function injectInternals(internals) {
if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === "undefined") {
return false;
}
const hook = __REACT_DEVTOOLS_GLOBAL_HOOK__;
if (hook.isDisabled) {
return true;
}
if (!hook.supportsFiber) {
return false;
}
try {
const rendererID = hook.inject(internals);
injectedHook = hook;
injectedProfilingHooks = hook.getProfilingHooks
? hook.getProfilingHooks()
: null;
return true;
} catch (err) {
return false;
}
}
function onCommitRoot(root, eventPriority) {
if (injectedHook && typeof injectedHook.onCommitFiberRoot === "function") {
try {
const didError = (root.current.flags & DidCapture) === DidCapture;
if (enableProfilerTimer) {
let schedulerPriority;
switch (eventPriority) {
case DiscreteEventPriority:
schedulerPriority = ImmediateSchedulerPriority;
break;
case ContinuousEventPriority:
schedulerPriority = UserBlockingSchedulerPriority;
break;
case DefaultEventPriority:
schedulerPriority = NormalSchedulerPriority;
break;
case IdleEventPriority:
schedulerPriority = IdleSchedulerPriority;
break;
default:
schedulerPriority = NormalSchedulerPriority;
break;
}
injectedHook.onCommitFiberRoot(
rendererID,
root,
schedulerPriority,
didError
);
} else {
injectedHook.onCommitFiberRoot(rendererID, root, undefined, didError);
}
} catch (err) {
// Catch all errors because it is unsafe to throw during the commit phase.
}
}
}const fiberPool = [];
const POOL_SIZE = 10;
function createFiber(tag, pendingProps, key, mode) {
let fiber = fiberPool.pop();
if (fiber !== undefined) {
// Reuse pooled fiber
fiber.tag = tag;
fiber.key = key;
fiber.elementType = null;
fiber.type = null;
fiber.stateNode = null;
fiber.return = null;
fiber.child = null;
fiber.sibling = null;
fiber.index = 0;
fiber.ref = null;
fiber.pendingProps = pendingProps;
fiber.memoizedProps = null;
fiber.updateQueue = null;
fiber.memoizedState = null;
fiber.dependencies = null;
fiber.mode = mode;
fiber.flags = NoFlags;
fiber.subtreeFlags = NoFlags;
fiber.deletions = null;
fiber.lanes = NoLanes;
fiber.childLanes = NoLanes;
fiber.alternate = null;
return fiber;
}
return new FiberNode(tag, pendingProps, key, mode);
}
function releaseFiber(fiber) {
if (fiberPool.length < POOL_SIZE) {
// Reset fiber for reuse
resetFiberProperties(fiber);
fiberPool.push(fiber);
}
}// Automatic batching in React 18
function batchedUpdates(fn, a, b) {
const prevExecutionContext = executionContext;
executionContext |= BatchedContext;
try {
return fn(a, b);
} finally {
executionContext = prevExecutionContext;
if (executionContext === NoContext) {
resetRenderTimer();
flushSyncCallbacks();
}
}
}
// Manual batching control
function flushSync(fn) {
const prevExecutionContext = executionContext;
const prevTransition = ReactCurrentBatchConfig.transition;
try {
ReactCurrentBatchConfig.transition = null;
executionContext |= BatchedContext;
if (fn) {
return fn();
}
} finally {
executionContext = prevExecutionContext;
ReactCurrentBatchConfig.transition = prevTransition;
if (executionContext === NoContext) {
resetRenderTimer();
flushSyncCallbacks();
}
}
}| API | Description | Usage Example |
|---|---|---|
useState |
State management hook | const [state, setState] = useState(initialValue) |
useEffect |
Side effect hook | useEffect(() => { /* effect */ }, [deps]) |
useReducer |
Complex state management | const [state, dispatch] = useReducer(reducer, initialState) |
useCallback |
Memoized callback | const memoizedCallback = useCallback(fn, [deps]) |
useMemo |
Memoized value | const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]) |
useRef |
Mutable ref object | const ref = useRef(initialValue) |
| API | Description | Usage Example |
|---|---|---|
createRoot |
Create root container | const root = createRoot(container) |
root.render |
Render element to root | root.render(<App />) |
flushSync |
Force synchronous update | flushSync(() => { setState(newState) }) |
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = useCallback(() => {
setCount((c) => c + 1);
}, []);
const decrement = useCallback(() => {
setCount((c) => c - 1);
}, []);
const reset = useCallback(() => {
setCount(initialValue);
}, [initialValue]);
return { count, increment, decrement, reset };
}
// Usage
function Counter() {
const { count, increment, decrement, reset } = useCounter(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
</div>
);
}const LazyComponent = React.lazy(() => import("./LazyComponent"));
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
}function SearchResults() {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleSearch = (newQuery) => {
setQuery(newQuery);
startTransition(() => {
// This update has lower priority
setResults(searchData(newQuery));
});
};
return (
<div>
<input
value={query}
onChange={(e) => handleSearch(e.target.value)}
placeholder="Search..."
/>
{isPending && <div>Searching...</div>}
<ResultsList results={results} />
</div>
);
}- Use React.memo for component memoization
- Implement useCallback for stable function references
- Apply useMemo for expensive calculations
- Leverage Suspense for code splitting
- Utilize Transitions for non-urgent updates
- Avoid creating objects in render
- Don't use array indices as keys
- Minimize useEffect dependencies
- Prevent unnecessary re-renders
- Handle error boundaries properly
# Install dependencies
npm install
# Start development server
npm run dev
# Run tests
npm test
# Build for production
npm run build// Enable React DevTools
if (typeof window !== "undefined" && window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.onCommitFiberRoot = (id, root) => {
console.log("Fiber root committed:", root);
};
}
// Performance profiling
function ProfiledApp() {
return (
<Profiler id="App" onRender={onRenderCallback}>
<App />
</Profiler>
);
}
function onRenderCallback(
id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime
) {
console.log("Render performance:", {
id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime,
});
}- Interruptible Rendering: Allows React to pause and resume work
- Priority-Based Scheduling: High-priority updates can interrupt low-priority ones
- Better Error Handling: Improved error boundaries and recovery
- Concurrent Features: Enables Suspense, Transitions, and other concurrent features
- Fine-Grained Control: Binary lanes provide precise priority management
- Efficient Merging: Bitwise operations for fast lane manipulation
- Scalable Design: Easy to add new priority levels
- Concurrent Safety: Thread-safe priority comparisons
- Performance: Single event listener per event type
- Memory Efficiency: Reduced memory usage for event handlers
- Dynamic Content: Handles dynamically added/removed elements
- Consistent Behavior: Cross-browser event normalization
- Follow existing code conventions
- Use meaningful variable and function names
- Add comprehensive comments for complex logic
- Maintain consistent indentation and formatting
- Write unit tests for new features
- Ensure integration tests pass
- Add performance benchmarks for critical paths
- Test error handling scenarios
- Update API documentation for changes
- Add code examples for new features
- Maintain architectural decision records
- Keep README.md current
- Server Components: React Server Components implementation
- Streaming SSR: Improved server-side rendering with streaming
- Concurrent Rendering: Enhanced concurrent features
- Performance Optimizations: Additional performance improvements
- WebAssembly Integration: Exploring WASM for performance-critical paths
- Worker Thread Support: Offloading work to web workers
- Advanced Scheduling: More sophisticated scheduling algorithms
- Memory Management: Improved memory usage patterns
This React 18 implementation demonstrates the sophisticated engineering behind modern React:
β
Complete Fiber Architecture - Interruptible, priority-based rendering
β
Full Hooks System - All major hooks with proper lifecycle management
β
Concurrent Features - Suspense, Transitions, and priority scheduling
β
Event System - Synthetic events with delegation and cross-browser compatibility
β
Advanced Scheduler - Time-slicing with priority queues and yielding
β
Error Handling - Comprehensive error boundaries and recovery mechanisms
β
Performance Optimizations - Memory management, batching, and memoization
β
DevTools Integration - Full debugging and profiling capabilities
- π Modular Architecture: Clean separation of concerns across packages
- β‘ Concurrent Rendering: Non-blocking, interruptible rendering pipeline
- π― Priority System: Sophisticated lane-based priority management
- π£ Hook System: Complete implementation of React's hook architecture
- π Performance: Optimized for memory usage and rendering performance
- π Developer Experience: Comprehensive debugging and development tools
By studying this implementation, developers gain deep insights into:
- Modern JavaScript framework architecture
- Concurrent programming patterns
- Performance optimization techniques
- Event system design
- State management strategies
- Error handling best practices
This implementation serves as both an educational resource and a foundation for understanding React's internal mechanisms, providing the knowledge needed to build high-performance React applications and contribute to the React ecosystem.
This project is licensed under the MIT License - see the LICENSE file for details.
This implementation is inspired by and follows patterns from the official React codebase. We acknowledge the React team's excellent work and design decisions that made this educational implementation possible.
React Team Contributors: Dan Abramov, Andrew Clark, Sebastian MarkbΓ₯ge, and the entire React core team for their innovative work on React's architecture and implementation.
Built with β€οΈ for the React community