- React
- Core React Hooks: State, Synchronization, and Refs
- useState Internals, Lazy Init & State Resetting
useState Internals, Lazy Init & State Resetting
useState persists state across renders in a linked list of hook slots attached to the component Fiber. Lazy initial state (useState(() => init())) runs expensive computations once on mount rather than on every render, and key props cleanly reset component state.
Key Learning Objectives
Understand how React stores hook state in an internal linked list on the Fiber node.
Implement lazy initial state to avoid expensive recalculations on every render.
Reset state cleanly when props change using the key prop pattern.
Avoid calling initial state calculation functions directly inside useState(init()).
The Interview Problem & Context
What is the difference between useState(expensiveComputation()) and useState(() => expensiveComputation())? Implement a stateful hook with lazy initialization and explain how React allocates state slots internally.
1function expensiveCompute(): number {2 // Heavy computation (e.g. parsing 5MB JSON from localStorage)3 return 42;4}56export function Counter() {7 // ❌ BAD: expensiveCompute() runs on EVERY single re-render of Counter!8 // const [count, setCount] = useState(expensiveCompute());910 // ✅ GOOD: Lazy Initializer: function only invoked ONCE during initial mount!11 const [count, setCount] = useState(() => expensiveCompute());1213 return <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>;14}
Deep Architectural Explanation
How useState Works Under the Hood
React keeps track of which hook belongs to which state value using an internal singly linked list of Hook objects stored on the component's Fiber node (fiber.memoizedState):
type Hook = {
memoizedState: any, // The current state snapshot
baseState: any,
queue: UpdateQueue, // Queued updates
next: Hook | null, // Pointer to the next hook in the component!
};Every time your component executes, React steps through this linked list from top to bottom. This is the fundamental reason behind the Rules of Hooks: you cannot call hooks conditionally or in loops, because doing so shifts the index pointers in the linked list!
The Cost of Direct Initializer Invocations
When you write:
const [data] = useState(parseLargeJson());In JavaScript, function arguments are evaluated before the function call. Even though React only uses the initial value on Mount 1 and discards it on every subsequent render, parseLargeJson() still runs on every single render!
By passing a callback:
const [data] = useState(() => parseLargeJson());React only invokes the function during the initial mount, saving CPU cycles on all subsequent renders.
Common Interview Traps & Pitfalls
Calling expensive functions directly inside useState: useState(localStorage.getItem(...)).
Using useEffect to reset state when an ID prop changes instead of using a key prop on the component.
Violating the Rules of Hooks by putting useState inside an if condition.
Senior Interviewer Follow-Up Probes
Why can't React use variable names to match hook states instead of call order?
What happens to the internal hook linked list if a component throws an error during render?
How does React 19 Action state (useActionState) build upon useState queue mechanics?
Frequently Asked Interview Questions
What happens if you call useState(expensive()) without a callback?
The expensive() function will execute on every single re-render of the component, wasting CPU time, even though React throws away its return value on all renders after the first.
Why do the Rules of Hooks require calling hooks at the top level?
Because React matches hook state to components using the fixed order of execution in an internal linked list. Conditional or looped calls disrupt the order, mapping state to the wrong hook slots.
How do you cleanly reset a component's state when a prop changes?
By changing the component's key prop (e.g., <Form key={userId} />). React treats a changed key as a new component identity, destroying the old Fiber state and mounting a fresh instance.
Can useState store a function as a state value?
Yes, but you must use a functional setter or nested function: setState(() => myFunction), otherwise React will interpret the function as an updater and execute it immediately.
