Purity, StrictMode and class components: why React renders twice in development
Components must be pure. Learn why StrictMode runs renders and effects twice in development, where side effects belong, the class lifecycle methods and their hook equivalents, and how to write an error boundary.
Comes up in: React
Learn#
Neha’s cart shows every item twice in development, but only once in production. She thinks React is broken. It is showing her a bug.
A pure component returns the same output for the same props and state and changes nothing outside itself. StrictMode helps you find impure ones: in development it calls each render twice and mounts each effect twice, so hidden side effects show up as duplicates.
import { GlobalRegistrator } from "@happy-dom/global-registrator";
GlobalRegistrator.register(); // a DOM for Node; in a browser you already have one
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
const { createElement: h, StrictMode, useEffect, act } = await import("react");
const { createRoot } = await import("react-dom/client");
const log = [];
const cart = [];
function Item({ name }) {
cart.push(name); // a side effect during render: a bug
log.push(`render ${name}`);
useEffect(() => {
log.push(`connect ${name}`);
return () => log.push(`disconnect ${name}`);
}, [name]);
return h("li", null, name);
}
const root = createRoot(document.createElement("ul"));
await act(() => root.render(h(StrictMode, null, h(Item, { name: "tea" }))));
console.log(log.join(" | "));
console.log("cart:", cart);Output (verified)
render tea | render tea | connect tea | disconnect tea | connect tea cart: [ 'tea', 'tea' ]
Rendering should only calculate. Side effects belong in event handlers, or in effects with a cleanup.
State
- cart: [tea, tea]
Console
- render tea
- render tea
StrictMode, development only. React calls Item's render twice and keeps one result. Both calls push to the shared cart array.
Read the steps as text
- StrictMode, development only. React calls Item's render twice and keeps one result. Both calls push to the shared cart array.
- React commits once and runs the effect: connect tea.
- StrictMode then simulates unmount and remount: cleanup runs, then the effect runs again.
- The duplicate in cart shows a real bug: rendering had a side effect. The effect pair proves the cleanup works.
Predict#
Predict first
Does StrictMode call your components twice in the production build?
The double render and the mount, unmount, mount of effects happen only in development. Production renders once. People remove StrictMode to “fix” the duplicates, which hides the bug instead.
Interview angle#
Say it like this — about 30 seconds
“Components must be pure: the same props, state and context give the same output, with no side effects during render. In development, StrictMode calls render functions twice, and mounts, unmounts and remounts effects, to expose code that is not pure or effects that do not clean up. Production runs once. Side effects belong in event handlers or in effects with cleanup. In class components, lifecycle methods map to hooks, and error boundaries are still written as classes.”
Follow-up Why does React StrictMode run effects twice in development?
Follow-up How do lifecycle methods map to hooks?
Follow-up How do you write an error boundary?
Practice#
Guided
Build a small purity checker, like StrictMode’s double render.
Done when
- Props are deep-copied and deep-frozen for each call.
- Mutating props gives “mutates props”; different results give “different output”.
- Pure renders return
{ pure: true }, and other errors are rethrown.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
What makes a component pure?
Same inputs give the same output, with no side effects during render.
What does StrictMode do to effects in development?
Mounts, unmounts and remounts them, to test cleanup.
Which lifecycle method maps to an effect's cleanup?
componentWillUnmount.
Which two class methods make an error boundary?
static getDerivedStateFromError and componentDidCatch.
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
Check yourself: Why is props.items.push(x) inside a component a bug?
It mutates input during render, which React assumes never happens.
How it works underneathSenior rounds
render, componentDidMount, componentDidUpdate, componentWillUnmount. Class state lives in this.state and changes with this.setState Source: React: Component (class API).Check yourself: Which lifecycle methods run after the component is in the DOM?
componentDidMount and componentDidUpdate.
Edge cases and trapsWhere people slip
React.memo for classes; shouldComponentUpdate does the same by hand.Check yourself: What does PureComponent compare?
Props and state, shallowly.
In productionStaff-level answers
Check yourself: Do error boundaries catch errors in event handlers?
No. Only errors during rendering, in lifecycle methods and in constructors below them.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- Why does React StrictMode run effects twice in development?