Skip to content

Commit dc2e82c

Browse files
committed
Add useState internal implementation
1 parent a4f162c commit dc2e82c

File tree

1 file changed

+149
-2
lines changed

1 file changed

+149
-2
lines changed

README.md

Lines changed: 149 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6204,7 +6204,7 @@ Technically it is possible to write nested function components but it is not sug
62046204
62056205
**[⬆ Back to Top](#table-of-contents)**
62066206
6207-
274. Can useState take a function as an initial value?
6207+
274. ### Can useState take a function as an initial value?
62086208
Yes, `useState` can take a function as an initial value, and this is a useful feature in React called **lazy initialization**. This function is also known as **initializer function**.
62096209
62106210
When you call useState(initialValue), you normally pass in a value directly:
@@ -6228,7 +6228,154 @@ Technically it is possible to write nested function components but it is not sug
62286228
```
62296229
62306230
**[⬆ Back to Top](#table-of-contents)**
6231-
275.
6231+
275. ### What types of values can `useState` hold?
6232+
6233+
The `useState` hook accepts different types of values.
6234+
6235+
* Primitives: `number`, `string`, `boolean`
6236+
* Arrays
6237+
* Objects
6238+
* Functions
6239+
* `null` or `undefined`
6240+
6241+
But you needs to be cautious with **reference types (objects/arrays)** because React compares old and new values **by reference**, so direct mutations won't trigger a re-render.
6242+
For example, the correct and wrong ways of state updates as shown below,
6243+
```js
6244+
user.name = "Sudheer"; //wrong way
6245+
setUser(prev => ({ ...prev, name: 'Sudheer' })); //correct way
6246+
```
6247+
**[⬆ Back to Top](#table-of-contents)**
6248+
276. ### What happens if you call `useState` conditionally?
6249+
As per rules of React Hooks, hooks must be called unconditionally. For example, if you conditionally call it:
6250+
```js
6251+
if (someCondition) {
6252+
const [state, setState] = useState(0);
6253+
}
6254+
```
6255+
6256+
React will throw a runtime error because it **relies on the order of Hook calls**, and conditional logic breaks that order.
6257+
**[⬆ Back to Top](#table-of-contents)**
6258+
277. ### Is useState Synchronous or Asynchronous?
6259+
The `useState` hook is synchronous, but state updates are asynchronous. When you call `useState()`, it runs synchronously and returns the state variable and setter function as tuple.
6260+
```js
6261+
const [count, setCount] = useState(0);
6262+
```
6263+
This happens immediately during rendering.
6264+
However, the state update function (**`**setState**`**) is asynchronous in the sense that it doesn't update the state immediately.
6265+
React **batches** updates and applies them before the next render. You won’t see the updated value immediately after calling `setState`.
6266+
**Example:**
6267+
```js
6268+
const [count, setCount] = useState(0);
6269+
6270+
function handleClick() {
6271+
setCount(count + 1);
6272+
console.log(count); // ❗️Still logs the old value
6273+
}
6274+
```
6275+
The > `console.log(count)` prints the **old value**, because the update hasn’t happened yet.
6276+
6277+
To see the updated state value, you can use `useEffect()` hook. It runs **after the component has re-rendered.**  By the time `useEffect` runs:
6278+
6279+
* The component has been updated.
6280+
* The **state contains the new value**.
6281+
6282+
```js
6283+
import React, { useState, useEffect } from 'react';
6284+
6285+
function Counter() {
6286+
const [count, setCount] = useState(0);
6287+
6288+
const handleClick = () => {
6289+
setCount(count + 1);
6290+
console.log('Clicked count (old):', count); // Old value
6291+
};
6292+
6293+
useEffect(() => {
6294+
console.log('Updated count:', count); // New value
6295+
}, [count]); // Only runs when `count` changes
6296+
6297+
return <button onClick={handleClick}>Count: {count}</button>;
6298+
}
6299+
```
6300+
**[⬆ Back to Top](#table-of-contents)**
6301+
6302+
278. ### Can you explain how useState works internally?
6303+
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:
6304+
6305+
#### 1. **Hook List / Linked List**
6306+
6307+
* React maintains a linked list or array of "hook states" for each component.
6308+
* When a component renders, React keeps track of which hook it is currently processing via a cursor/index.
6309+
* Each call to `useState()` corresponds to one "slot" in this list.
6310+
6311+
#### 2. **State Storage**
6312+
6313+
* Each slot stores:
6314+
* The current state value.
6315+
* A queue of pending state updates.
6316+
6317+
#### 3. **Initial Render**
6318+
6319+
* When the component first renders, React:
6320+
* Creates a new slot for `useState` with the initial state (e.g., `0`).
6321+
* Returns `[state, updaterFunction]`.
6322+
6323+
#### 4. **Updater Function**
6324+
6325+
* The updater function (`setCount`) is a closure that, when called:
6326+
* Enqueues a state update to React's internal queue.
6327+
* Schedules a re-render of the component.
6328+
6329+
#### 5. **Re-render and State Update**
6330+
6331+
* On the next render:
6332+
* React processes all queued updates for each hook slot.
6333+
* Updates the stored state value accordingly.
6334+
* Returns the new state to the component.
6335+
6336+
#### 6. **Important: Hook Order**
6337+
6338+
* Hooks must be called in the same order on every render so React can match hook calls to their internal slots.
6339+
* That’s why you can’t call hooks conditionally.
6340+
6341+
The pseudocode for internal implementation of `useState` looks like below,
6342+
```js
6343+
let hookIndex = 0;
6344+
const hooks = [];
6345+
6346+
function useState(initialValue) {
6347+
const currentIndex = hookIndex;
6348+
6349+
if (!hooks[currentIndex]) {
6350+
// First render: initialize state
6351+
hooks[currentIndex] = {
6352+
state: initialValue,
6353+
queue: [],
6354+
};
6355+
}
6356+
6357+
const hook = hooks[currentIndex];
6358+
6359+
// Process queued updates
6360+
hook.queue.forEach(update => {
6361+
hook.state = update(hook.state);
6362+
});
6363+
hook.queue = [];
6364+
6365+
// Define updater function
6366+
function setState(action) {
6367+
// action can be new state or function(state) => new state
6368+
hook.queue.push(typeof action === 'function' ? action : () => action);
6369+
scheduleRender(); // triggers React re-render
6370+
}
6371+
6372+
hookIndex++;
6373+
return [hook.state, setState];
6374+
}
6375+
```
6376+
6377+
**[⬆ Back to Top](#table-of-contents)**
6378+
62326379
## Old Q&A
62336380
62346381
1. ### Why should we not update the state directly?

0 commit comments

Comments
 (0)