Chapter 18: Keeping Components Pure
Core Idea
React assumes every component is a pure function of its props/state: same inputs, same JSX output, and no mutation of anything that existed before the call — like a math formula, not a recipe that changes ingredients as it goes.
Key Concepts
- Pure function, two properties: (1) minds its own business — doesn't mutate objects/variables that existed before it ran; (2) same inputs → same output, always, regardless of when or how many times it's called.
- Rendering must stay pure: a component should only return JSX from its inputs (props, state, context) — never write to an outer variable during render. Reading and incrementing a module-level
let guest counter inside a component body is the canonical example of breaking this: multiple renders produce different, unpredictable output, and the bug gets worse if other components also read that variable.
- Fix pattern: turn the mutated outer value into a prop or piece of state passed explicitly, instead of an implicit shared variable read during render.
- Local mutation is fine: creating a variable during the same render and mutating only that (e.g. building up an array inside the component body before returning it) doesn't violate purity — the rule is about not touching state that predates this render call.
- Side effects belong outside rendering: things like DOM mutation, network requests, timers, or
console.log-driven side effects don't belong in the render body itself — they belong in event handlers or useEffect (Ch 36), triggered by rendering, not happening during it.
- Strict Mode helps catch impurity: React calls each component function twice in development under
<StrictMode> specifically to surface impure renders — if double-invoking a component produces visibly different results, that's a purity bug to fix, not a Strict Mode quirk to silence.
Code Examples
// Impure: mutates a variable that existed before this render
let guest = 0;
function Cup() {
guest = guest + 1; // 🔴 side effect during render
return <h2>Tea cup for guest #{guest}</h2>;
}
// Pure: guest count comes in as a prop
function Cup({ guest }) {
return <h2>Tea cup for guest #{guest}</h2>;
}
- What it demonstrates: the exact fix for the classic impurity bug — move the value that used to be mutated externally into an explicit prop.
Key Takeaways
- Never write to a variable that existed before the current render — if a component needs to "remember" or "count," that's what state (Ch 21) or props are for, not module-level mutable variables.
- Strict Mode's double-invoke-in-dev behavior isn't overhead to work around — it's a bug detector for exactly this class of impurity.
- Purity is what makes React's rendering model reason-able and safe to memoize/reorder/re-run — an impure component silently breaks assumptions that features like Strict Mode, memoization, and the React Compiler all rely on.
Connects To
- Ch 22 (Render and Commit): where "rendering" sits in React's overall update cycle, and why it must stay pure.
- Ch 36 (Synchronizing with Effects): where side effects (the things purity excludes from render) are supposed to live instead.
- Ch 42 (React Compiler Introduction): the compiler's automatic memoization relies on components being pure — impure components can produce wrong optimized output.