Хуки React: useState, useEffect, useRef и другие на примерах
Практика3 минРедакция Innovialabs
Коротко
Хуки — это функции, которые дают компоненту состояние (useState), побочные эффекты (useEffect) и ссылки (useRef); вызывайте их только на верхнем уровне и честно указывайте зависимости.
Что такое хуки и зачем они нужны
Хуки — это функции, которые позволяют функциональному компоненту хранить состояние, реагировать на изменения и работать с DOM. Они заменили классовые компоненты и сделали логику переиспользуемой: общий код выносится в свой хук, а не размазывается по жизненному циклу.
Основной набор, который нужен почти в каждом проекте:
| Хук | Для чего |
|---|---|
useState | Локальное состояние, изменение которого вызывает перерисовку |
useEffect | Синхронизация с внешним миром: подписки, таймеры, запросы |
useRef | Ссылка на DOM-элемент или значение, которое не вызывает ререндер |
useMemo / useCallback | Кеширование вычислений и функций между рендерами |
useContext | Чтение значения из контекста без передачи пропсов |
useState: состояние компонента
const [count, setCount] = useState(0);
// Новое значение зависит от предыдущего — используйте функцию
setCount(prev => prev + 1);
Важные моменты:
- Состояние неизменяемо. Для объектов и массивов создавайте копию:
setUser({ ...user, name }), а неuser.name = name. - Обновление асинхронно. Сразу после
setCountпеременнаяcountв текущем рендере остаётся старой. - Дорогое начальное значение передавайте функцией:
useState(() => parse(data))— она вызовется один раз.
useEffect и массив зависимостей
useEffect запускает код после отрисовки. Второй аргумент определяет, когда эффект повторяется:
- без массива — после каждого рендера;
[]— один раз после монтирования;[userId]— при измененииuserId.
useEffect(() => {
const controller = new AbortController();
fetch(`/api/users/${userId}`, { signal: controller.signal })
.then(r => r.json())
.then(setUser)
.catch(() => {});
return () => controller.abort(); // очистка
}, [userId]);
Функция, которую возвращает эффект, — очистка. Она вызывается перед следующим запуском и при размонтировании. Здесь отменяйте запросы, снимайте подписки и останавливайте таймеры.
Типичные ошибки с useEffect
- Пропущенные зависимости. Если эффект использует переменную, она должна быть в массиве. Включите правило
react-hooks/exhaustive-depsв ESLint — оно ловит это автоматически. - Бесконечный цикл. Эффект меняет состояние, от которого сам зависит, или в зависимостях объект, создаваемый заново в каждом рендере.
- Эффект для вычислений. Если значение выводится из пропсов или состояния, считайте его прямо в теле компонента, без
useEffectи лишнегоuseState. - Нет очистки. Подписки и интервалы без очистки приводят к утечкам и двойным обработчикам.
- Гонка запросов. Без отмены ответ на старый запрос может прийти позже нового и перезаписать данные.
useRef: DOM и значения без ререндера
const inputRef = useRef(null);
// <input ref={inputRef} />
inputRef.current.focus();
ref.current можно менять, и это не вызывает перерисовку. Поэтому useRef подходит для хранения id таймера, предыдущего значения или флага, который не нужно показывать на экране.
Правила хуков
- Вызывайте хуки только на верхнем уровне компонента — не внутри условий, циклов и вложенных функций. React опирается на порядок вызовов.
- Вызывайте хуки только из компонентов или других хуков, а не из обычных функций.
Свои хуки
Свой хук — это функция с именем на use, внутри которой вызываются другие хуки. Он выносит повторяющуюся логику:
function useDebounce(value, delay) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const id = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(id);
}, [value, delay]);
return debounced;
}
Каждый компонент, вызвавший хук, получает своё состояние — хуки делят логику, а не данные.