⚛️ The Pure Function Lie
React's central claim: UI is a pure function of state. Render the same state, get the same UI. Elegant. Mathematical. Wrong.
The key prop is the proof
If UI were truly a pure function of state, you wouldn't need keys. You'd render a list, React would know what changed, done. But DOM elements have identity. A text input with focus and a cursor position is not interchangeable with another text input that happens to have the same props. A list item mid-animation is not the same as a fresh list item in the same position. The DOM node is something — not just a description of something.
Keys are React admitting the model is broken. "Please tell me which nodes are which, because I can't figure it out from the render output alone." A truly pure system wouldn't need that hint. The output would determine the identity. Instead, you hand-annotate identity because the framework's core abstraction can't derive it.
Every hook is a patch
It cascades from there.
useRef — "I need to touch the real DOM because the abstraction can't express what I need."
useEffect — "Something needs to happen that isn't a function of props."
useMemo, useCallback — "Every render creates fresh object references that invalidate dependency arrays, firing effects again, causing state updates, triggering more renders. Without these, the pure function silently becomes an infinite loop."
useState — "I need state, but there's nowhere to put it except inside the render function, which is supposed to be pure."
Every hook is a patch over the fundamental mismatch between "UI is a pure function" and "a UI is a running program with its own uncontrolled state."
The negation problem
Here's a real one. You're building a rule builder — a segment editor where conditions can be included or excluded. The segment is a function of an AST. To negate a rule, you wrap it in a <NegatedContext>. This is pure logic with no visual representation, but in React there is nothing but the render tree, so the logic goes into the render tree.
The children unmount and remount. Different component instance, different DOM nodes. You want to animate the toggle between included and excluded with a CSS transition. You can't. CSS transitions need the same DOM element transitioning between states. React destroyed the element and created a new one because the logical structure changed.
The workaround is a library like framer-motion with AnimatePresence — hundreds of kilobytes to fake continuity between different DOM nodes by measuring positions and creating synthetic animations. Hundreds of kilobytes to work around the framework destroying the node you wanted to animate.
The fundamental problem: React conflates logical structure with physical structure. Changing the meaning destroys the markup. In a real UI, those are independent. You can negate a rule without destroying the card it's rendered in.
The honest alternative
Is the UI a pure function? No. Is the code as pretty as React? No. But a web component that manages its own state, handles its own DOM, and responds to attribute changes is easy to understand, performs well, and can be thrown away without ceremony.
Flip an attribute — negated="true" — and the component handles it internally. CSS sees the same element. Transitions work. The logical change is data. The physical structure is untouched. No keys. No refs. No effects. No lie.
React's API is an abstraction over something it fundamentally misunderstands. The DOM is not a render target. It's a living, stateful, interactive program. Better to embrace that than fight it with a model that needs an escape hatch on every other line.