Home/Learn/React/StrictMode, Purity and Concurrent Rendering

StrictMode, Purity and Concurrent Rendering

Advanced
State & Effects

React assumes your components are pure and may render them more than once, or abandon a render entirely. StrictMode makes violations visible in development.

Overview

React reserves the right to call your component function multiple times, to start a render and throw it away, and to interleave rendering with other work. All of that depends on one contract: rendering is pure — same inputs, same output, no side effects along the way. Most code satisfies this accidentally, which is why violations only surface as intermittent bugs. StrictMode double-invokes components and effects in development specifically to turn those intermittent bugs into consistent ones you notice immediately.

What Purity Means Here

Rendering must not change anything outside the component. These are the usual violations.

No mutation, no DOM writes, no randomness in render
// Impure — mutating a prop or module variable during render
let renderCount = 0
function Bad({ items }) {
  renderCount++                     // side effect during render
  items.sort()                      // mutating a prop
  document.title = 'Problems'       // DOM write during render
  return <List items={items} />
}

// Pure
function Good({ items }) {
  const sorted = [...items].sort()
  useEffect(() => { document.title = 'Problems' }, [])
  return <List items={sorted} />
}

// Impure in a subtler way — the output depends on something that
// can change between two calls with the same props:
function Row() { return <li>{Math.random()}</li> }
function Row2() { return <li>{new Date().toISOString()}</li> }
// Both break double-render checks and server rendering (hydration
// mismatch: the server produced one value, the client another).

What StrictMode Does

Development-only double-invocation, and what each doubling is testing for.

It is a detector, not a nuisance
// main.jsx
<StrictMode><App /></StrictMode>

// In development it:
//   - calls component functions twice        -> exposes impure renders
//   - calls setup, cleanup, setup on effects -> exposes missing cleanup
//   - double-invokes reducers and useMemo    -> exposes impure logic
//   - warns about deprecated APIs

// Two console logs from one render is expected, not a bug.
// Two POST requests from one effect IS a bug — in the effect.

// None of this happens in production builds.

// The wrong response, seen constantly in real codebases:
const done = useRef(false)
useEffect(() => {
  if (done.current) return          // suppresses the symptom
  done.current = true
  init()
}, [])
// The remount that happens on a route change in production will
// break this anyway. Write cleanup instead.

Concurrent Rendering

Why the purity rules got stricter, and the two hooks that use the capability directly.

useTransition and useDeferredValue
// React can interrupt a render to handle something more urgent,
// then resume or discard it. A discarded render must leave no trace —
// hence the purity contract.

// useTransition — mark an update as interruptible so typing stays
// responsive while an expensive list re-renders
const [isPending, startTransition] = useTransition()

function onChange(e) {
  setQuery(e.target.value)                 // urgent: the input
  startTransition(() => setFilter(e.target.value))  // can be interrupted
}
{isPending && <Spinner />}

// useDeferredValue — the same idea, without controlling the setter
const deferredQuery = useDeferredValue(query)
const results = useMemo(() => search(deferredQuery), [deferredQuery])

// Both matter when a large list re-renders on every keystroke.
// Neither makes the work faster — they make it non-blocking.

Key Points to Remember

  • 1React assumes rendering is pure and may call a component twice or discard a render entirely
  • 2Mutating props, writing to the DOM, or using Math.random during render all break that contract
  • 3StrictMode double-invokes components and effects in development to expose impurity and missing cleanup
  • 4Suppressing the double-invoke with a ref hides a bug that returns on any real remount
  • 5useTransition and useDeferredValue keep the UI responsive by marking updates as interruptible

Interview Questions

Sign in to ask Aria
1

What does it mean for a React component to be pure?

Medium
2

Why does StrictMode intentionally double-invoke your components?

Medium
3

What problem does useTransition solve?

Hard

Ask Aria about StrictMode, Purity and Concurrent Rendering

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…