Engrain by ByteRush

RCT.01.03 Mid-level· 12 minCore — asked often

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);
This example uses Node.js, so it runs in our checks rather than in your browser.

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.

What StrictMode does on mountStep 1 of 4

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
  1. StrictMode, development only. React calls Item's render twice and keeps one result. Both calls push to the shared cart array.
  2. React commits once and runs the effect: connect tea.
  3. StrictMode then simulates unmount and remount: cleanup runs, then the effect runs again.
  4. 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?

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.”

Stays on your device. Nothing is uploaded.
Follow-up Why does React StrictMode run effects twice in development?
To check that every effect cleans up correctly, since React can unmount and remount components, for example with Activity or Fast Refresh.
Follow-up How do lifecycle methods map to hooks?
componentDidMount and componentDidUpdate become useEffect; componentWillUnmount becomes the effect cleanup; shouldComponentUpdate becomes React.memo.
Follow-up How do you write an error boundary?
A class with static getDerivedStateFromError (to show a fallback) and componentDidCatch (to log), or a library such as react-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.
Your solution
Ctrl/⌘ + Enter

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
Render with what you are given: never write to variables outside the component, never change props or state objects, and never start network requests during render.
Mutating props or state also breaks memo and change detection, because the old and new objects are the same.
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
A lifecycle method is a class method React calls at a fixed time: 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

PureComponent skips rendering when props and state are shallowly equal, like React.memo for classes; shouldComponentUpdate does the same by hand.
Mutating an object and setting it again defeats both, because the reference did not change.

Check yourself: What does PureComponent compare?

Props and state, shallowly.

In productionStaff-level answers

An error boundary catches errors thrown while rendering its children and shows a fallback. It does not catch errors in event handlers or async code Source: React: Catching rendering errors with an error boundary.
Place one per independent area, such as each dashboard widget.

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?