Skip to main content

Command Palette

Search for a command to run...

From Render to Commit: How React Thinks

A mental model for understanding React's execution flow, effects, and re-renders

Published
4 min read
F

Hey! I'm Faiaz — a frontend developer who loves writing clean, efficient, and readable code (and sometimes slightly chaotic code, but only when debugging). This blog is my little corner of the internet where I share what I learn about React, JavaScript, modern web tools, and building better user experiences. When I'm not coding, I'm probably refactoring my to-do list or explaining closures to my cat. Thanks for stopping by — hope you find something useful or mildly entertaining here.

React is often taught as a set of hooks and APIs.

useState, useEffect, props, context — you learn what they do, you build things, and everything seems fine.

Until one day, something feels off.

A console.log prints an old value. A button click doesn’t behave how you expect.
A useEffect runs twice. A component re-renders when “nothing changed.“

At that point, you realize the problem isn’t syntax.

It’s that you don’t yet understand how React thinks.

This article explains React from the inside out — not a a library of hooks, but as an execution engine with a very specific mental model.


React Is an Execution Model, Not a Framework

The most important idea to internalize is this:

React does not execute your code top-to-bottom like normal JavaScript.

A React component is not a script. It’s a description.

When React runs your component, it asks a single questions:

Given the current state and props, what should the UI look like right now?

Everything else — hooks, effects, re-renders — exists to support that question.


The Three Phases of React

React operates in three distinct phases:

  1. Render Phase — calculate what the UI should be

  2. Commit Phase — apply changes to the DOM

  3. Effect Phase — run side effects (useEffect)

Most confusion comes from mixing these phases together.

Let’s separate them.


The Render Phase: Pure Calculation

The render phase is where React reads you components.

During render:

  • State values are read

  • Props are read

  • Context value are read

  • JSX is evaluated

  • Functions are created

  • console.log runs

But importantly:

No DOM updates happen here.

Think of render as React doing math.

No side effects. No mutations. Just calculation.


Why State Doesn’t Update Immediately

Consider this code:

setCount(count + 1)
console.log(count)

Many expect the new value to log. It doesn’t.

Why?

Because setState does not mutate state.

It schedules an update.

React finishes the current render first, then decides when to re-render.

This is not a bug. It’s what allows React to batch updates and stay fast.


Closures: Every Render Is a Snapshot

Every render creates a new version of your component.

That means every function you define:

const handleClick = () => {
    console.log(count)
}

captures the value of count from that render.

This is why you sometimes see “stale“ values.

React is not confused.

You are just calling a function created in a previous snapshot.


The Commit Phase: Making It Real

Once React finishes calculating what changed, it enters the commit phase.

This is where:

  • DOM updates happen

  • Elements appear, disappear, or change

The commit phase is fast and synchronous.

After this, the UI you see on screen is finally updated.


useEffect: After the World Settles

useEffect runs after the commit phase.

That’s why effects:

  • See updated DOM

  • Can safely access layout

  • Should be used for side effects

Dependency rules:

  • [] → run once after first render

  • [value] → run when value changes

  • no array → run after every render

Effects do not block rendering. They are scheduled.


Why Effects Cause Extra Renders

This pattern is common:

useEffect(() => {
    setCount(c => c + 1)
}, [])

Execution order:

  1. Initial render

  2. Commit

  3. Effect runs

  4. State Updates

  5. Re-render

That second render is expected.

Understanding this removes a lot of “why is this running twice?“ confusion.


Props Are Derived, Not Stored

Props are calculated during render:

<Child total={count + tax} />

When either count or tax changes:

  • Parent re-renders

  • Child re-renders

Props are not memory. They are output.


Context: Invisible Global Triggers

Context is global state.

When a context value changes:

  • Every consuming component re-renders

Even if their props didn’t change.

This explains many “unnecessary“ re-renders.
They are not unnecessary. They are the cost of global state.


Events Don’t Interrupt React

User actions follow this order:

  1. Event fires

  2. Handler runs

  3. State updates are scheduled

  4. React decides when to re-render

Multiple clicks may be batched.

React owns the timeline.


How to Read React Code Correctly

When reading React code, always ask:

  1. Is this render logic or effect logic?

  2. What values exist in this render snapshot?

  3. What schedules a re-render?

  4. What runs after commit?

If you answer these, output becomes predictable.


Why This Mental Model Transfers Beyond React

Once you understand React’s execution model, other frameworks become easier.

Vue, for example, has:

  • Reactive state

  • Dependency tracking

  • Lifecycle hooks

  • Virtual DOM

Different syntax. Same ideas.

Frameworks change. Execution models don’t.


Final Thought

React feels unpredictable only until you understand how it thinks.

Once you do:

  • State updates make sense

  • Effects stop being mysterious

  • Output questions become mechanical

React is not magic.

It’s just very disciplined about when it runs your code.

And once you align your thinking with that discipline, everything clicks.