From Render to Commit: How React Thinks
A mental model for understanding React's execution flow, effects, and re-renders
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:
Render Phase — calculate what the UI should be
Commit Phase — apply changes to the DOM
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.logruns
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 changesno 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:
Initial render
Commit
Effect runs
State Updates
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:
Event fires
Handler runs
State updates are scheduled
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:
Is this render logic or effect logic?
What values exist in this render snapshot?
What schedules a re-render?
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.



