fw-02 — Analysis

Required invariants

  1. Elements are plain data. Descriptions, not DOM nodes; the runtime decides what to do with them.
  2. Same type and key ⇒ reuse the DOM node. Node identity is what carries state.
  3. Identity must be author-supplied for lists. The runtime cannot derive it.
  4. Hook order is stable per component instance. Positional storage requires it.
  5. Effects clean up before the next run, and on unmount.

Why keys cannot be inferred

before: [A, B, C]
after:  [B, C]

Deleted A? Or renamed A→B, B→C, deleted the third? Both are consistent with the data, and they imply different DOM operations and different state outcomes. This is information-theoretic, not an implementation limitation.

key={index} restores positional correspondence — it is not a weak key, it is no key with extra steps, and it fails identically on reorder, prepend, and delete-from-middle.

The measurement that settles the virtual-DOM argument

The claim is not "diffing beats DOM mutation." It is:

build description + diff + minimal mutations  <  mutations a naive implementation performs

That inequality holds against innerHTML replacement and fails against "I know exactly which text node changed." Which is why fine-grained reactive systems can win — they do know.

The spec measures the left-hand side directly: one change in a 200-item list must cost ≤5 DOM operations. A rebuild does ~200.

Failure modes

BreakConsequence
Unkeyed reorderstate follows position, not identity — inputs keep the wrong values
Index as keysame bug, disguised
Conditional hook callslot list shifts; every subsequent hook reads the wrong state
Missing effect dependencystale closure reads a value from a previous render
Missing cleanupsubscriptions accumulate across remounts

Every value in a component body is a snapshot of one render. Internalising that sentence resolves most useEffect confusion, and it is a better model than "add the dependency."