Inside React’s useEffect: A Deep Dive into the Fiber Source Code
This article dissects the inner workings of React's useEffect hook by tracing its implementation through the React Fiber architecture, from mountEffect and updateEffect functions to the commit phase, revealing how side‑effects are scheduled, created, and cleaned up in modern React applications.
Overview of useEffect in React
The article explains why
useEffectwas introduced, how it replaces lifecycle methods in class components, and how it separates side‑effects from rendering logic.
Mount and Update Implementations
Both
mountEffectand
updateEffectare defined in
ReactFiberHooks.js. They create a new hook, compute
nextDeps, set the
sideEffectTag, and store an
Effectobject via
pushEffect. When dependencies have not changed,
updateEffectuses
NoHookEffectto skip execution.
pushEffect Function
pushEffect(tag, create, destroy, deps)builds an
Effectnode (a circular linked list) and adds it to the component's
updateQueue. The queue is a simple object with a
lastEffectpointer, forming a circular list of effects for the component.
Component Update Queue
The
componentUpdateQueueholds the effect list. When the first effect is added, the queue is initialized and its
lastEffectpoints to the new effect; subsequent effects are linked in a circular fashion.
Render Flow from ReactDOM to Fiber
Rendering starts with
ReactDOM.render, which calls
updateContainerin
ReactFiberReconciler.js. This function creates a root update and schedules work via
scheduleWork. The scheduler eventually invokes
renderRoot, which runs the
workLoop(or
workLoopSync) to process units of work.
performUnitOfWork and beginWork
performUnitOfWorkrepeatedly calls
beginWorkfor each fiber. For function components,
beginWorkexecutes
renderWithHooks, which runs the component function, registers hooks, and builds the effect list.
Commit Phase
When the render phase finishes,
commitRootis called. It extracts the effect list from the finished work, then runs three phases:
commitBeforeMutationEffects commitMutationEffects commitLayoutEffectsEach phase iterates over the same circular effect list.
commitHookEffectList
The core of
useEffecthandling resides in
commitHookEffectList(unmountTag, mountTag, finishedWork). It retrieves the component's
updateQueue, walks the circular list of
Effectobjects, and for each effect:
If the
tagmatches
unmountTag, it calls the stored
destroyfunction.
If the
tagmatches
mountTag, it calls the stored
createfunction and saves its return value as the new
destroyfunction.
Effects with the
NoHookEffecttag are ignored, which explains why some hooks do nothing in certain phases.
Key Takeaways
The article demonstrates how React transforms a declarative
useEffectcall into a low‑level effect list, schedules it through the Fiber work loop, and finally executes or cleans up side‑effects during the commit phases. Understanding this pipeline clarifies the behavior of dependency arrays, cleanup functions, and the timing of effect execution.
For a visual overview of the Fiber render and commit phases, see the diagram from the React Conf 2017 talk "A Cartoon Intro to Fiber".
QQ Music Frontend Team
QQ Music Web Frontend Team
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.