Logo

CopyPastWorld

Обучение программированию

Полное руководство по хукам React: useState, useEffect, useMemo и useCallback

Практическое руководство по хукам React 18/19: управление состоянием, жизненный цикл, предотвращение лишних ререндеров и разбор частых ошибок с зависимостями.

Обновлено: 05.10.2026•Время чтения: 10 мин•Команда CopyPastWorld
#React#JavaScript#Frontend#Хуки
Тренажер React

Модуль 14: Хуки React на практике

Пишите реальные компоненты с хуками и проверяйте работу стейта через интерактивные тесты.

Попробовать в тренажере →
Размер шрифта для чтения:100% (16px)

Хуки появились в React 16.8 и полностью изменили разработку компонентов: они позволяют использовать локальное состояние, побочные эффекты и оптимизацию внутри функциональных компонентов без написания классов.


1. Фундаментальные правила хуков

Прежде чем писать код, необходимо соблюдать два строгих соглашения React:

Два золотых правила хуков:

  1. Вызывайте хуки только на самом верхнем уровне: не вызывайте хуки внутри циклов (for), условных операторов (if) или вложенных функций. Порядок вызова хуков обязан быть строго идентичным при каждом рендере.
  2. Вызывайте хуки только из функций React: из функциональных компонентов или ваших собственных кастомных хуков.

2. useState: Управление локальным состоянием

Хук useState сохраняет значение между перерисовками компонента и предоставляет функцию обновления:

import { useState } from 'react';

export function Counter() {  
  const [count, setCount] = useState<number>(0);

  const increment = () => {  
    // Безопасное обновление через коллбэк, если новое значение зависит от предыдущего:  
    setCount(prev => prev + 1);  
  };

  return (  
    <button onClick={increment} className="btn-primary">  
      Количество кликов: {count}  
    </button>  
  );  
}

Совет эксперта:
Если начальное состояние требует тяжелых вычислений (например, чтение и парсинг из localStorage), передавайте функцию инициализатора:
const [data, setData] = useState(() => loadExpensiveData());
Функция выполнится ровно один раз при монтировании, а не при каждом рендере.


3. useEffect: Побочные эффекты и синхронизация

useEffect выполняет сетевые запросы, подписки на внешние хранилища, таймеры и манипуляции с DOM:

import { useState, useEffect } from 'react';

export function UserProfile({ userId }: { userId: string }) {  
  const [user, setUser] = useState<{ name: string } | null>(null);

  useEffect(() => {  
    let isCancelled = false;

    async function loadData() {  
      const res = await fetch(`/api/users/${userId}`);  
      const data = await res.json();  
      if (!isCancelled) {  
        setUser(data);  
      }  
    }

    loadData();

    // Функция очистки (cleanup) вызывается перед размонтированием или повторным запуском эффекта  
    return () => {  
      isCancelled = true;  
    };  
  }, [userId]); // Эффект перезапустится только при изменении userId

  return <div>{user ? user.name : 'Загрузка...'}</div>;  
}

Режимы массива зависимостей

Массив зависимостейКогда срабатывает эффектСценарий использования
Не указан (useEffect(fn))После каждого рендера компонентаЛогирование, отслеживание любых перерисовок
Пустой массив ([])Ровно один раз при монтированииПодписка на глобальные события окна, разовый fetch
С зависимостями ([id, filter])При монтировании и при смене указанных переменныхЗагрузка данных под конкретный ID, пересчет фильтров

Анатомия сбоя:
Самая частая причина бесконечного цикла рендеров — передача нестабильного объекта или стрелочной функции в массив зависимостей без мемоизации. React сравнивает зависимости по ссылке (Object.is), поэтому объект, созданный прямо в теле функции, всегда считается «новым».


4. useMemo и useCallback: Производительность без фанатизма

Оба хука предназначены для мемоизации (кэширования), но работают с разными объектами:

КритерийuseMemouseCallback
Что возвращаетРезультат вычисления функцииСсылку на саму функцию
Главная цельНе пересчитывать тяжелые алгоритмыНе менять ссылку на функцию между рендерами
Типовой примерФильтрация и сортировка 10 000 строкПередача колбэка в дочерний компонент с React.memo
// useMemo кэширует вычисленное значение:  
const sortedProducts = useMemo(() => {  
  return products.filter(p => p.inStock).sort((a, b) => b.price - a.price);  
}, [products]);

// useCallback кэширует неизменную ссылку на функцию:  
const handleSelect = useCallback((id: string) => {  
  console.log('Выбран элемент:', id);  
}, []);  

Совет эксперта:
Не оборачивайте каждую функцию подряд в useCallback. Сам по себе хук требует ресурсов процессора и памяти. Мемоизация оправдана только при замерах или передаче колбэка в тяжелые компоненты, защищенные через React.memo.


5. Резюме: Чек-лист чистых хуков

  • Всегда объявляйте хуки на верхнем уровне функции.
  • Всегда возвращайте функцию очистки (cleanup) из useEffect, если создаете таймер или слушатель событий.
  • Включайте все переменные из области видимости компонента в массив зависимостей.
  • Оптимизируйте производительность через useMemo и useCallback осознанно.

Понравилась шпаргалка?

В платформе CopyPastWorld собрано более 400 интерактивных уроков с практическими заданиями, автотестами и поддержкой сообщества.