Когда в 2018 вышли React Hooks, многие восприняли их как синтаксический сахар. Сейчас же классовые компоненты выглядят legacy-решением, а Hooks стали стандартом. Но так ли они универсальны? Разберём на практике.
Из чего сделаны Hooks
Hooks — это не магия, а продуманный механизм работы с Fiber — внутренней реализацией React. Каждый хук привязывается к конкретному компоненту через dispatcher, что объясняет их ключевые ограничения:
- Только на верхнем уровне — нельзя вызывать в условиях или циклах, потому что React полагается на порядок вызова хуков для сопоставления состояния между рендерами.
- Только внутри компонентов — вызов вне функционального компонента сломает связь с Fiber-нодой.
Пример проблемного кода:
if (isAuth) {
const [user] = useState(null); // 🔴 Wrong: conditional hook
}
Главные хуки в работе
useState — базовый хук для состояния. Под капотом использует очередь обновлений, поэтому асинхронные изменения могут вести себя неожиданно:
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1); // ✅ Работает
setCount(count + 1); // ❌ Не сработает — берёт замороженное значение
setCount(prev => prev + 1); // ✅ Корректно — получает актуальное состояние
};
useEffect — хук для сайд-эффектов. Главная боль — dependency array, где легко ошибиться:
useEffect(() => {
fetchData();
}, []); // 🔴 Частая ошибка: забыли зависимости
useEffect(() => {
const timer = setInterval(() => {}, 1000);
return () => clearInterval(timer); // ✅ Cleanup обязателен
}, []);
Когда Hooks не панацея
- Сложная бизнес-логика — Custom Hooks помогают, но при глубокой вложенности лучше подходят Zustand или Redux.
- Производительность — useMemo/useCallback требуют ручной оптимизации, тогда как классы давали shouldComponentUpdate.
- Старые проекты — миграция с классов на хуки часто требует полного рефакторинга, а не точечных правок.
Что попробовать
- useReducer для сложного состояния вместо useState.
- useLayoutEffect когда нужны синхронные изменения DOM.
- React-Query или SWR вместо ручных эффектов для данных.
Hooks — это не “лучше или хуже”, а другой подход. Они сокращают boilerplate, но требуют понимания closures и потоков данных. Для новых проектов — безусловный выбор, в legacy-коде стоит оценивать затраты на миграцию.