Frontend Development 18 min read

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.

QQ Music Frontend Team
QQ Music Frontend Team
QQ Music Frontend Team
Inside React’s useEffect: A Deep Dive into the Fiber Source Code

Overview of useEffect in React

The article explains why

useEffect

was introduced, how it replaces lifecycle methods in class components, and how it separates side‑effects from rendering logic.

Mount and Update Implementations

Both

mountEffect

and

updateEffect

are defined in

ReactFiberHooks.js

. They create a new hook, compute

nextDeps

, set the

sideEffectTag

, and store an

Effect

object via

pushEffect

. When dependencies have not changed,

updateEffect

uses

NoHookEffect

to skip execution.

pushEffect Function

pushEffect(tag, create, destroy, deps)

builds an

Effect

node (a circular linked list) and adds it to the component's

updateQueue

. The queue is a simple object with a

lastEffect

pointer, forming a circular list of effects for the component.

Component Update Queue

The

componentUpdateQueue

holds the effect list. When the first effect is added, the queue is initialized and its

lastEffect

points 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

updateContainer

in

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

performUnitOfWork

repeatedly calls

beginWork

for each fiber. For function components,

beginWork

executes

renderWithHooks

, which runs the component function, registers hooks, and builds the effect list.

Commit Phase

When the render phase finishes,

commitRoot

is called. It extracts the effect list from the finished work, then runs three phases:

commitBeforeMutationEffects
commitMutationEffects
commitLayoutEffects

Each phase iterates over the same circular effect list.

commitHookEffectList

The core of

useEffect

handling resides in

commitHookEffectList(unmountTag, mountTag, finishedWork)

. It retrieves the component's

updateQueue

, walks the circular list of

Effect

objects, and for each effect:

If the

tag

matches

unmountTag

, it calls the stored

destroy

function.

If the

tag

matches

mountTag

, it calls the stored

create

function and saves its return value as the new

destroy

function.

Effects with the

NoHookEffect

tag are ignored, which explains why some hooks do nothing in certain phases.

Key Takeaways

The article demonstrates how React transforms a declarative

useEffect

call 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".
Fiber render and commit phases
Fiber render and commit phases
javascriptReactHooksFiberuseEffect
QQ Music Frontend Team
Written by

QQ Music Frontend Team

QQ Music Web Frontend Team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.