How React's useState, Fragment, and Lazy Init Map Directly to Native DOM Behavior
Understanding that React's abstractions are thin wrappers over native browser behavior—not magic—lets developers debug closure traps, avoid wasted re-computation, and reason about rendering without guessing. The DocumentFragment-to-Fragment mapping and the hooks linked-list walkthrough turn opaque framework behavior into inspectable mechanics.
Starting from raw `createElement` and `appendChild` calls, the progression moves through `DocumentFragment` as a batch-rendering container—an invisible wrapper that disappears after mounting its children—and then maps that pattern directly onto React's `<Fragment>` component. The same "memory vs. DOM tree" distinction underpins React's virtual DOM diffing.
The useState deep-dive unpacks why three consecutive `setCount(count + 1)` calls only increment by one: each call captures the same closure value, so React's update queue sees identical values and overwrites them. Switching to the functional updater `prev => prev + 1` chains the updates correctly because React feeds each function the previous state. The explanation then traces how the hooks linked list on the Fiber node stores state and why the call order must never change.
Lazy initialization gets the same treatment. `useState(heavyComputation())` runs the expensive function on every render because JavaScript evaluates arguments before the call; `useState(() => heavyComputation())` passes a function reference that React calls only once. The controlled-component breakdown follows a single keystroke from the native input event through React's synthetic event wrapper, into the state update queue, and back out to the DOM, showing exactly where React intercepts the browser's default behavior.
The entire lesson is structured as a chain of "why" questions that force each React abstraction to justify itself against a native counterpart, which is a more durable mental model than memorizing API shapes.
Calling `setState` is synchronous enqueuing, not an async operation—the state value only updates on the next render, which explains why `console.log` immediately after always prints the old value.
React's synthetic event `e.target` still points to the real DOM node, so `e.target.value` reads directly from the browser's input; React only wraps the event object, not the target.
The lazy-initialization function runs at most once, so using it for non-deterministic values like `Math.random()` is pointless—the result is frozen from the first render.