Когда в 2018 году вышли хуки, документация React уверяла: “они просто работают”. Но когда в продакшене начали всплывать баги типа “Rendered fewer hooks than expected”, стало ясно — под капотом происходит что-то нетривиальное.
Хуки — это не магия, а call stack
Первый миф, который нужно развенчать: хуки не привязаны к компоненту. На самом деле React опирается на порядок вызовов. Вот что происходит при рендере:
- React начинает выполнение функционального компонента
- Каждый вызов
useState/useEffectпомещает данные в стек - После рендера стек сверяется с предыдущим состоянием
function BuggyComponent() {
if (Math.random() > 0.5) {
useState(); // Хук 1
}
useState(); // Хук 2
}
Этот код будет падать, потому что между рендерами меняется количество хуков. React хранит состояние в виде связного списка, где каждый хук знает о следующем.
Почему хуки работают только на верхнем уровне
Правило “не вызывайте хуки внутри условий” — не прихоть React Team, а следствие реализации. Представьте:
// Первый рендер
useState('A'); // Запоминаем как hook 1
useState('B'); // hook 2
// Второй рендер
if (condition) {
useState('A'); // hook 1
}
// Где hook 2? Ломаем всю цепочку
Вот как это выглядит в псевдокоде React:
let currentHook = 0;
let hooks = [];
function useState(initial) {
hooks[currentHook] = hooks[currentHook] || initial;
const setStateHookIndex = currentHook;
function setState(value) {
hooks[setStateHookIndex] = value;
render();
}
return [hooks[currentHook++], setState];
}
Подводные камни замыканий
Самый коварный баг — “stale closure” в useEffect. Проблема в том, что эффекты захватывают значения на момент создания:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
console.log(count); // Всегда 0!
}, 1000);
return () => clearInterval(id);
}, []); // Пустой массив зависимостей
return <button onClick={() => setCount(c => c + 1)}>+</button>;
}
Решение — либо добавлять count в зависимости, либо использовать функциональную форму setCount.
Хуки и текущий стек технологий
С появлением Server Components модель хуков усложнилась. Теперь нужно учитывать:
- Хуки не работают в Server Components (по определению)
useStateна сервере — это вообще другой механизм- Next.js 13+ добавляет hydration warnings для несоответствий
Вот как выглядит типичный баг при миграции:
'use client'; // Без этой директивы хуки не работают
function ClientComponent() {
const [state] = useState(); // Теперь ок
return <div>{state}</div>;
}
Что пробовать в 2024
usehook (экспериментальный) — попытка сделать хуки более гибкими- React Forget — компилятор, который может устранить need for
useMemo/useCallback - Библиотеки типа
usehooks-tsс готовыми паттернами
Хуки — это мощно, но не бесплатно. Их абстракция протекает в сложных сценариях, и понимание механики спасает от часов дебагга. Главное правило: если хук ведёт себя странно, ищите изменение порядка вызовов.
Источник: https://youtu.be/FneS7tCWBMU