TGViewer
8BitJS 8BitJS @eightbitjs · 162 subscribers
Post #11 532
​​Как работает useState. Упрощенная реализация

Рассмотрим реализацию useState, чтобы понять базовые принципы его работы.
Весь код можно посмотреть в sandbox, сейчас же разберем по порядку.

Для начала нам нужны глобальные переменные:

let currentlyRenderingComponent = null;
let currentHookIndex = 0;
const componentHooks = new Map();
let pendingUpdates = [];


currentlyRenderingComponent указывает на компонент, который сейчас рендерится.
currentHookIndex отслеживает порядок хуков внутри компонента.
componentHooks связывает компоненты с их хуками.
pendingUpdates имитирует очередь на обновление.

Теперь создадим необходимые структуры данных:

class Hook {
constructor(initialState) {
this.memoizedState = initialState;
this.baseState = initialState;
this.queue = [];
}
}

class Update {
constructor(action) {
this.action = action;
this.next = null;
}
}


Класс Hook представляет собой одно состояние внутри компонента. У каждого хука есть значение и очередь обновлений. Также мы хранить исходное значение, для сбросов или вычислений.

Класс Update описывает отдельное обновление состояния и формирует связанный список, чтобы обновления можно было применять по очереди.

Вспомогательная функция для получения или создания хука:

function getOrCreateHook(index) {
let hooks = componentHooks
.get(currentlyRenderingComponent);

if (!hooks) {
hooks = [];
componentHooks
.set(currentlyRenderingComponent, hooks);
}

if (index >= hooks.length) {
hooks.push(new Hook());
}

return hooks[index];
}


Эта функция отвечает за доступ к нужному хуку в массиве хуков компонента. Если массива или самого хука еще нет, они создаются. Это важно для поддержки последовательного вызова хуков.

function dispatchAction(component, hook, action) {
const update = new Update(action);
hook.queue.push(update);

if (!pendingUpdates.includes(component)) {
pendingUpdates.push(component);
}

scheduleUpdate();
}


Функция dispatchAction имитирует поведение setState: она создает объект обновления, добавляет его в очередь соответствующего хука и инициирует обновление компонента.
В упрощенном понимании scheduleUpdate имитирует постановку задач на выполнение через отложенный render с setTimeout.

Реализация useState:

function useState(initialState) {
const hook = getOrCreateHook(currentHookIndex++);

if (hook.queue.length) {
let next = hook.baseState;
for (const item of hook.queue)
next = typeof item.action === "function"
? item.action(next)
: item.action;
hook.queue.length = 0;
hook.memoizedState = next;
hook.baseState = next;
} else if (hook.memoizedState === undefined) {
const value =
typeof initialState === "function"
? initialState()
: initialState;
hook.memoizedState = value;
hook.baseState = value;
}

const owner = currentlyRenderingComponent;
const dispatch = (action) => dispatchAction(owner, hook, action);

return [hook.memoizedState, dispatch];
}


Сначала получаем хук по текущему индексу — если такого еще нет, он создается. 
Проверяем очередь хуков, если в ней накопились обновления, они применяются одно за другим: если обновление — это функция, то она вызывается с текущим значением, иначе используется переданное значение напрямую. После обработки обновлений очередь очищается, и новое значение сохраняется как актуальное состояние. 

Если это первый вызов useState и хук еще не был инициализирован, то начальное состояние устанавливается из initialState — это может быть как значение, так и функция (ленивая инициализация). 

Затем создается функция dispatch, связанная с текущим компонентом и конкретным хуком, чтобы при вызове setState обновлялось только нужное состояние. В конце возвращается пара, аналогичная настоящему useState: текущее значение состояния и функция для его обновления.

---
#React #Hooks #useState #Fiber #Frontend #JavaScript #8bitJS
  • 🔥 5
  • 👍 1
More from @eightbitjs
  1. Jul 21, 2026​​Ускоряем O(N + T), не меняя Big O. Часть 5. Зачем здесь WebAssembly После реализации бак…
  2. Jul 18, 2026​​Итоги CodeRun Summer: 15 задач и 636 попыток решения Финал CodeRun Summer Challenge выгл…
  3. Jul 17, 2026​​Ускоряем O(N + T), не меняя Big O. Часть 4. Кэш, бакеты и упаковка событий В предыдущей…
  4. Jul 16, 2026​​Ускоряем O(N + T), не меняя Big O. Часть 3. Как оставить четыре байта и не сломать ответ…
  5. Jul 15, 2026​​Ускоряем O(N + T), не меняя Big O. Часть 2. Четыре байта, которые могут изменить результ…
  6. Jul 14, 2026​​Ускоряем O(N + T), не меняя Big O. Часть 1. Разностный массив — это только начало На про…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →