React Architecture & Virtual DOM

React is a declarative, component-based UI library that keeps an in-memory representation (the Virtual DOM) of the UI, computes what changed, and then efficiently patches the browser DOM — while using a scheduler (Fiber) to prioritize and break work into interruptible chunks.

Analogy :
Imagine a model train set (DOM — physical layout) and a miniature blueprint (Virtual DOM). You adjust the blueprint; before you touch the real layout, you compare the new blueprint with the old one and only move the few train pieces that need updating. That saves time and avoids breaking the whole set.

Core building blocks

Components

  • The units of UI: small, reusable pieces (functions or classes) that return what the UI should look like given props + state.

  • Components form a tree (root → children → grandchildren).

Props & State

  • Props: read-only inputs from parent to child.

  • State: component-local data that triggers re-render when changed.

  • React enforces one-way data flow (parent → child).

Render output: Virtual DOM (VDOM)

  • A lightweight JavaScript object tree that mirrors the UI structure (elements, props, children).

  • Not the real DOM — an in-memory description of what the DOM should look like.

What is the Virtual DOM — simple and precise

  • Virtual DOM = an object-based representation of DOM nodes (type, props, children).

  • It allows React to:

    1. Compute the next UI as pure JS (no costly DOM reads/writes during calculation).

    2. Compare old VDOM and new VDOM cheaply (diffing) to find minimal changes.

    3. Apply a small set of real DOM operations (patch) to update the UI.

Why not always update the real DOM directly?
Because manipulating the DOM is relatively slow. By batching and only applying necessary mutations, React improves perceived and actual performance.

How the Virtual DOM update works — step-by-step (render → diff → commit)

  • Trigger — an update is requested (e.g., setState, new props from parent).

  • Render phase (build new VDOM) — React re-executes render functions (or class render) to produce the new VDOM tree. This phase is pure (no side-effect DOM writes) and can be interrupted.

  • Diffing / Reconciliation — React compares the previous VDOM tree to the new one to determine the minimal changes needed.

  • Commit phase — React applies the computed DOM updates (insert, update attributes, remove) to the real DOM and runs lifecycle side-effects (e.g., useEffect, componentDidMount). The commit phase is synchronous and must complete before the browser repaints.

The Reconciliation algorithm: the main ideas

React’s reconciliation uses heuristics (rules of thumb) to efficiently compare trees rather than trying to solve an expensive perfect-diff:

  • Same component type: If the node type (e.g., 'div' or MyComponent) is the same, React keeps the same underlying instance and only updates props/children as needed.

  • Different types: If types differ, React tears down the old subtree and mounts the new one.

  • Lists & keys: For arrays of children, keys are used to match items between renders. Keys should be stable, unique IDs (not indices) so React can reorder/match correctly and minimize DOM ops.

  • Heuristic behaviour: When keys are missing or unstable, React falls back to index-based matching which can cause unnecessary DOM churn.

Tip: Always provide stable key props for list items — that’s the single most common reconciliation helper.

React Fiber — why it matters (modern internals)

React Fiber (React 16+) is a reimplementation of React’s core renderer that introduced:

  • Incremental rendering: Breaking rendering work into units so React can pause, yield to higher-priority work (like user input), and resume.

  • Prioritization: Updates can be prioritized (urgent vs. low priority).

  • Concurrency groundwork: Fiber enables features like Suspense and the concurrent rendering model (React’s newer concurrent features let React prepare multiple versions of the UI).

Important concept: Fiber separates the render phase (can be interrupted) from the commit phase (must be synchronous). This improves responsiveness on complex UIs.

Lifecycle, Hooks and where they run

Class components (classic lifecycle)

  • render() produces VDOM.

  • componentDidMount, componentDidUpdate, componentWillUnmount run during/after commit or unmount.

Function components + Hooks (modern)

  • useState, useReducer provide state.

  • useEffect runs after the commit phase (side effects).

  • useLayoutEffect runs synchronously after DOM mutations but before the browser paints (useful for measuring DOM).

  • Hooks make render logic predictable and easier to reuse.

Teacher note: Effects can see the updated DOM because they run after commit.

Server-Side Rendering (SSR) & Hydration

  • SSR: React components are rendered to HTML on the server and sent to the client for faster first paint and SEO.

  • Hydration: On the client, React attaches event listeners and rehydrates the HTML into a live React app — it must match the server HTML closely to avoid extra work or warnings.

Practical: Use frameworks (Next.js, Remix) for robust SSR + routing + code splitting.

Common performance optimizations (practical checklist)

  • Use keys for lists.

  • Avoid expensive work inside render — keep render pure and fast.

  • Memoize components with React.memo (pure functional component) or PureComponent for classes.

  • Use useMemo / useCallback for expensive computed values or stable callbacks — but measure first.

  • Code-splitting: React.lazy + Suspense for lazy-loaded chunks.

  • Virtualize long lists (react-window, react-virtualized).

  • Profile with React DevTools Profiler and browser performance tools.

  • Avoid unnecessary re-renders: lift state smartly, split components, avoid passing new references on every render.

  • Batch updates (React batches state updates automatically in event handlers).

  • For heavy computations, move to web workers or memoized values.

Small code example + explanation (what happens internally)

function Counter() {
  const [count, setCount] = React.useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(c => c + 1)}>+1</button>
    </div>
  );
}

What happens when the button is clicked:

  1. setCount schedules a state update.

  2. React enters the render phase: it calls Counter() to produce a new VDOM (with the new count).

  3. React diffs the new VDOM with the previous VDOM, sees only the <p> text changed.

  4. Commit phase: React updates the text node in the real DOM.

  5. After commit, React runs effects (none in this example).

Scroll to Top