React & Modern Frontend (445 Qs)•Part 9: React Hooks Deep Dive (262 - 316)
Can you explain how useState works internally?
React’s hooks, including useState, rely on some internal machinery that keeps track of state per component and per hook call during rendering. Here's a simplified explanation of the internal mechanics:
1. Hook List / Linked List
- React maintains a linked list or array of "hook states" for each component.
- When a component renders, React keeps track of which hook it is currently processing via a cursor/index.
- Each call to
useState()corresponds to one "slot" in this list.
2. State Storage
- Each slot stores:
- The current state value.
- A queue of pending state updates.
3. Initial Render
- When the component first renders, React:
- Creates a new slot for
useStatewith the initial state (e.g.,0). - Returns
[state, updaterFunction].
4. Updater Function
- The updater function (
setCount) is a closure that, when called: - Enqueues a state update to React's internal queue.
- Schedules a re-render of the component.
5. Re-render and State Update
- On the next render:
- React processes all queued updates for each hook slot.
- Updates the stored state value accordingly.
- Returns the new state to the component.
6. Important: Hook Order
- Hooks must be called in the same order on every render so React can match hook calls to their internal slots.
- That’s why you can’t call hooks conditionally.
The pseudocode for internal implementation of useState looks like below,
js
let hookIndex = 0;
const hooks = [];
function useState(initialValue) {
const currentIndex = hookIndex;
if (!hooks[currentIndex]) {
// First render: initialize state
hooks[currentIndex] = {
state: initialValue,
queue: [],
};
}
const hook = hooks[currentIndex];
// Process queued updates
hook.queue.forEach(update => {
hook.state = update(hook.state);
});
hook.queue = [];
// Define updater function
function setState(action) {
// action can be new state or function(state) => new state
hook.queue.push(typeof action === 'function' ? action : () => action);
scheduleRender(); // triggers React re-render
}
hookIndex++;
return [hook.state, setState];
}