Explain why React expects rendering to be a pure computation (UI = f(props, state, context)) and why side effects must be moved to effects/event handlers. Connect it to render vs commit, Concurrent Rendering, StrictMode double-invocation, memoization, and predictable reconciliation. Pure rendering improves predictability and testing, but side effects must live in effects/hooks. Test with StrictMode and rerenders.
Why are React components expected to be pure functions of props and state?
Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.
Core idea
React wants rendering to be a deterministic computation: given the same props + state (+ context), the component should return the same UI description. That purity lets React freely re-run renders, pause/abort work, and diff trees safely without causing “real-world” side effects.
Why purity matters | What it enables in React | What breaks if you add side effects in render |
|---|---|---|
Render can run multiple times | Retry / discard renders safely (especially in Concurrent Rendering) | Duplicate API calls, duplicate subscriptions, duplicated logs, inconsistent state |
Render can be paused / resumed / interleaved | Scheduling + prioritization without committing to the DOM | Effects happen at the wrong time, race conditions, tearing-like bugs |
React uses diffing + memoization | Predictable reconciliation, | Non-deterministic output makes “same inputs => same UI” false |
StrictMode intentionally stress-tests | Double-invocation in dev helps catch unsafe patterns | Impure render causes “works in prod, weird in dev” behavior |
Render vs side effects
Render phase should only compute the next UI tree. Side effects belong in event handlers (user actions) or effects (useEffect/useLayoutEffect) which run after React commits changes.
// ❌ Impure render: side effects inside render
let renders = 0;
function Profile({ user }) {
renders++; // side effect: mutating external state
// side effect: mutating props object
user.lastSeenAt = Date.now();
// side effect: starting async work during render
fetch('/api/track?u=' + user.id);
return <div>Renders: {renders} — {user.name}</div>;
}
Side effect in render | Why it’s bad | Where it should go instead |
|---|---|---|
Mutating props/state/external variables | Repeated renders produce cumulative mutations and hard-to-debug bugs | Compute derived values immutably; update state via setters |
Network calls / subscriptions / timers | Render may be retried/aborted → duplicated work and leaks | useEffect cleanup; subscribe/unsubscribe there |
Reading time/randomness (Date.now, Math.random) | Same inputs can yield different UI (non-deterministic) | Initialize once with lazy state init, or store in state |
DOM reads/writes | DOM may not match the tree until commit; causes layout issues | useLayoutEffect (reads/writes after commit) or refs |
// ✅ Pure render + side effects moved out
function Profile({ user }) {
// Pure: derived UI only
return <div>{user.name}</div>;
}
function ProfileWithTracking({ user }) {
React.useEffect(() => {
let cancelled = false;
(async () => {
try {
await fetch('/api/track?u=' + user.id);
} finally {
// optional: handle completion
}
})();
return () => {
cancelled = true; // example cleanup flag
};
}, [user.id]);
return <Profile user={user} />;
}
Rule of thumb
If running the component body twice would cause anything “real” to happen twice (API call, subscription, mutation, analytics), it’s not pure. Keep render as a calculation; do real work in effects and event handlers.
Practical scenario
A product card renders purely from props and local state, while data fetching happens in effects.
Common pitfalls
- Triggering side effects during render.
- Deriving state incorrectly from props.
- Relying on mutable props.
Pure renders are predictable but require disciplined effects. Test with StrictMode and snapshot behavior.
React expects components to be pure so it can re-run rendering freely (including retries/aborts), apply concurrent scheduling, and reconcile efficiently. Side effects in render break determinism and lead to duplicated work and inconsistent UI; move them to effects/event handlers where React guarantees timing relative to commits.