tezvyn:

Why pass a function to useState for expensive initial values?

AI-drafted, machine-checkedSource: react.devintermediate
Why pass a function to useState for expensive initial values?

This tests your understanding of React's render-phase behavior. Without the function, expensive work reruns on every render and is thrown away; the initializer runs once only during mount.

WHAT THIS TESTS: This question probes whether you understand the boundary between React's initialization phase and its render phase. It checks if you know that useState receives its initial argument expression during every render but only applies it on the first mount, and whether you can articulate why lazy initialization matters for performance and correctness. Interviewers want to hear that you think about work that happens inside the render path.

A GOOD ANSWER COVERS: First, the direct call computeExpensiveValue(props) executes on every render because JavaScript evaluates arguments before invoking the Hook. React ignores the returned value after the first mount, so the work is completely wasted. Second, the initializer function form useState(() => computeExpensiveValue(props)) is called only once by React during the initial render, and its return value is stored as the state. Third, the initializer must be pure and must take no arguments, because React may call it twice in Strict Mode during development and will discard one of the results. Fourth, this pattern is distinct from useMemo; useMemo still runs during every render to compare dependencies, whereas the initializer is strictly mount-only and avoids even that overhead.

COMMON WRONG ANSWERS: Saying the function form prevents re-renders. Claiming it is merely syntactic sugar or a style preference without mentioning wasted computation on every render. Confusing the initializer with the updater function form of setState. Asserting that the initializer receives props or arguments, which the API explicitly does not support. Arguing that useMemo is the better tool here without acknowledging that useMemo still executes during render and incurs dependency comparison overhead. Saying React calls the initializer on every render but caches the result, which is false.

LIKELY FOLLOW-UPS: How does Strict Mode affect the initializer function, and why does React call it twice in development? What happens if the initializer is not pure? Why not just use useMemo instead? How would you initialize state from expensive props-derived data without repeating the work? What is the real performance cost if the computation takes 50 milliseconds and the component re-renders ten times? When is lazy initialization unnecessary?

ONE CONCRETE EXAMPLE: Imagine a dashboard widget that parses a two-megabyte JSON blob to build a lookup map. Calling useState(buildMap(data)) executes the parser on every render, costing 40 milliseconds each time even though React throws the result away after the first call. With useState(() => buildMap(data)), React invokes the arrow function once during mount, stores the map, and never calls it again. Over ten re-renders triggered by hover or polling updates, the lazy version saves 400 milliseconds of main-thread work and avoids jank.

Source: react.dev

Read the original → react.dev

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.