Why React? — Mental Model & Architecture
Before writing a single line of React, you need to understand WHY it exists and HOW it thinks about UIs. React's core insight is simple: UI is a function of state — `UI = f(state)`. Once you internalize this mental model, everything else — hooks, reconciliation, concurrent rendering — falls into place naturally.
The Core Problem React Solves
Before React, developers manually synchronized DOM updates with application state. Every data change meant hunting down every DOM node and updating it. This produced brittle, hard-to-test code that broke in unpredictable ways. React introduces a declarative model: you describe WHAT the UI should look like for any given state, and React figures out HOW to update the DOM efficiently. This separation is the foundation of all modern frontend frameworks.
React only updates what changed — not the entire page
The Component Tree Mental Model
- React applications are trees of components — each component is a function that takes inputs (props) and returns a description of UI (JSX)
- Components are composable: small, focused components combine to build complex interfaces
- Data flows DOWN the tree (parent → child via props) — this makes data flow predictable and traceable
- Events flow UP the tree (child notifies parent via callback props) — this keeps state ownership clear
- The root component (usually <App />) is the entry point; React mounts it into a real DOM node
- React never touches the real DOM directly — it works through a Virtual DOM layer for performance
Virtual DOM & Reconciliation — How React Updates the Screen
// React's rendering pipeline (simplified):
// 1. You call setState() or a state-setting function
// 2. React schedules a re-render of the affected component
// 3. React calls your component function again → gets new JSX (Virtual DOM tree)
// 4. React DIFFS the new tree against the previous tree (reconciliation)
// 5. React computes the minimum set of real DOM changes (the "patch")
// 6. React applies only those changes to the real DOM (commit phase)
// Example: Only the <span> changes, React leaves the <h1> untouched
function Counter({ count }) {
return (
<div>
<h1>My Counter</h1> {/* unchanged — React skips it */}
<span>{count}</span> {/* changed — React updates only this */}
</div>
);
}
// React Fiber: React 16+ internal architecture
// - Renders in small "units of work" (fibers) that can be interrupted
// - Allows React to pause, resume, or discard renders
// - Foundation of Concurrent Mode (React 18+)Declarative vs Imperative — The Paradigm Shift
// ❌ IMPERATIVE — you describe HOW to update the DOM step by step
function updateCounterImperative(newCount) {
const el = document.getElementById('counter');
el.textContent = newCount;
if (newCount > 10) {
el.style.color = 'red';
} else {
el.style.color = 'black';
}
}
// ✅ DECLARATIVE (React) — you describe WHAT the UI should look like
function Counter({ count }) {
return (
<span style={{ color: count > 10 ? 'red' : 'black' }}>
{count}
</span>
);
// React figures out HOW to make this happen
}
// The declarative approach is:
// - Easier to reason about (no hidden mutation)
// - Easier to test (pure function: same inputs → same output)
// - Safer to compose (no order-of-operations bugs)When to Use React (and When Not To)
- ✅ USE React for: SPAs with complex, dynamic UI; dashboards; design systems; apps with rich client-side state
- ✅ USE React for: teams that need consistent component patterns across many developers
- ✅ USE React + Next.js for: SEO-critical apps, content sites, full-stack applications needing SSR/SSG
- ⚠️ CONSIDER alternatives for: simple static sites (Astro, plain HTML), small utility apps (vanilla JS is fine)
- ⚠️ React adds bundle overhead (~45KB min+gzip) — not always justified for trivial UIs
- 📌 Key insight: React is a UI library, not a full framework — you assemble it with routing (React Router), state (Zustand/RTK), fetching (TanStack Query), etc.
Common Mistakes — Mental Model
- Treating React like jQuery: trying to directly manipulate DOM nodes while using React — this breaks reconciliation
- Thinking setState() is synchronous: state updates are batched and async in React 18 — reading state immediately after setState gives stale value
- Mutating state directly (e.g. `arr.push(item)`) instead of creating new references — React will not detect the change and won't re-render
- Confusing re-render with DOM update: a re-render is React calling your function again to compute new JSX; an actual DOM update only happens if the diff shows changes
Tip
Tip
When learning React, focus on the mental model first — UI = f(state). Once you internalize that components are pure functions of their inputs, hooks and patterns become intuitive rather than magical.
Practice Task
Note
Practice Task — (1) Create a bare React component that accepts a 'name' prop and renders a greeting. (2) Nest two components and pass data from parent to child. (3) Observe what happens in the browser DevTools React tab.
Quick Quiz
Common Mistake
Warning
When learning React, focus on the mental model first — UI = f(state). Once you internalize that components are pure functions of their inputs, hooks and patterns become intuitive rather than magical.
Key Takeaways
- Before writing a single line of React, you need to understand WHY it exists and HOW it thinks about UIs.
- React applications are trees of components — each component is a function that takes inputs (props) and returns a description of UI (JSX)
- Components are composable: small, focused components combine to build complex interfaces
- Data flows DOWN the tree (parent → child via props) — this makes data flow predictable and traceable