React question detail
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,
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];
}