Lazy State Initialization in React

Lazy state initialization lets React compute an initial state value through a function when the state is initialized, rather than evaluating an expensive expression during every component render. It is useful when deriving initial state requires noticeable computation or work.

With `useState`, you can provide either an initial value or an initializer function. For example, `useState(expensiveCalculation())` evaluates `expensiveCalculation()` whenever the component function executes, while `useState(() => expensiveCalculation())` gives React a function it can call to obtain the initial state.

The initializer is intended for computing the initial state, not for recalculating state on every render. After initialization, updates through the state setter determine the state value; ordinary re-renders do not rerun the initializer to replace that state.

Lazy initialization is most useful when the initial value is expensive to calculate, such as parsing substantial data or performing a costly transformation. For cheap expressions, using a lazy initializer can add unnecessary indirection and make the code harder to read.

In development, React Strict Mode may call initializer functions more than once to help detect accidental impurities. Therefore, an initializer should be pure: it should compute and return a value without relying on side effects such as mutations, network requests, or subscriptions.

Key points

Interview questions

What is lazy state initialization in React?

Lazy state initialization means passing a function to `useState` that computes the initial state. React can call that function to obtain the initial value instead of requiring the expression to be evaluated during every component execution. This is particularly useful for expensive initial calculations. The initializer should remain pure because React may invoke it more than once in development.

What is the difference between `useState(expensive())` and `useState(() => expensive())`?

In the first form, `expensive()` is evaluated as the component function executes, so the calculation can happen on every render. In the second form, the function itself is supplied as the initializer, allowing React to use it to obtain the initial state. This distinction matters when the calculation is costly. The lazy form avoids repeating that initial-state calculation merely because the component re-rendered.

Does a lazy initializer run on every render?

No. Its purpose is to provide the initial state value. Once the state has been initialized, subsequent renders use the existing state rather than replacing it by rerunning the initializer. Development Strict Mode can cause additional initializer calls during development checks, so the initializer still needs to be pure.

Why should a lazy state initializer be pure?

React expects rendering-related calculations to be safe to repeat because development checks can invoke them more than once. An initializer with side effects could therefore perform an operation multiple times or produce inconsistent state. Examples include mutating external objects, starting subscriptions, or making network requests. Such work belongs in an appropriate effect or event-driven operation rather than state initialization.