PurpleSchool — курсы программирования онлайн
  • Пути
    • Frontend React разработчик
    • Frontend Vue разработчик
    • Backend разработчик Node.js
    • Fullstack разработчик React / Node.js
    • Mobile разработчик React Native
    • Backend разработчик Golang
    • Devops инженер
    • Backend разработчик Python
  • AI для кодаНовое
  • О нас
    • Отзывы
    • Реферальная программа
    • О компании
    • Контакты
  • Иконка открытия меню
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
PurpleSchool — платформа бесплатных roadmap и курсов для разработчиков
ютуб иконка
Telegram иконка
VK иконка
VK иконка
Курсы
ГлавнаяКаталог курсовFrontendBackendFullstack
Практика
КарьераПроектыPurpleПлюс
Материалы
БлогБаза знаний
Документы
Договор офертаПолитика конфиденциальностиПроверка сертификатаМиграция курсовРеферальная программа
Реквизиты
ИП Ларичев Антон АндреевичИНН 773373765379contact@purpleschool.ru

PurpleSchool © 2020 -2026 Все права защищены

  • Курсы
    • FrontendИконка стрелки
    • AI разработкаИконка стрелки
    • BackendИконка стрелки
    • DevOpsИконка стрелки
    • MobileИконка стрелки
    • ТестированиеИконка стрелки
    • Soft-skillsИконка стрелки
    • ДизайнИконка стрелки
    Иконка слояПерейти в каталог курсов
  • Бесплатно
    • Курсы
    • JavaScript Основы разработкиPython Основы PythonCSS CSS FlexboxКарта развитияВопросы для собеседований
    • База знанийИконка стрелки
    • Новостные рассылкиИконка стрелки
  • PurpleSchool — курсы программирования онлайн
    • AI для кодаНовое
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
    Главная
    Сообщество
    React useState и useEffect: полное руководство 2024

    React useState и useEffect: полное руководство 2024

    Аватар автора React useState и useEffect: полное руководство 2024

    Антон Ларичев

    Иконка календаря29 июля 2026
    ReactuseStateuseEffectхукиJavaScriptFrontendjuniorИконка уровня junior
    Картинка поста React useState и useEffect: полное руководство 2024

    Введение

    useState и useEffect — два самых важных хука в React. Они появились в версии 16.8 и полностью изменили подход к написанию компонентов: теперь не нужны классы, чтобы работать с состоянием и жизненным циклом. В этой статье разберём оба хука детально — от базового синтаксиса до типичных ошибок.

    Хук useState

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

    import { useState } from 'react';
    
    function Counter() {
      // начальное значение счётчика — 0
      const [count, setCount] = useState(0);
    
      return (
        <div>
          <p>Счётчик: {count}</p>
          <button onClick={() => setCount(count + 1)}>Увеличить</button>
          <button onClick={() => setCount(count - 1)}>Уменьшить</button>
        </div>
      );
    }
    

    Обновление на основе предыдущего значения

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

    // Так делать не стоит — count может быть устаревшим
    setCount(count + 1);
    
    // Правильный способ — используем актуальное предыдущее значение
    setCount(prev => prev + 1);
    

    Состояние с объектом

    useState работает с любым типом данных. При работе с объектами важно помнить: React сравнивает состояния по ссылке, поэтому нужно создавать новый объект при каждом обновлении:

    function UserForm() {
      const [user, setUser] = useState({ name: '', email: '' });
    
      const handleNameChange = (e) => {
        // создаём новый объект, копируем старые поля и перезаписываем нужное
        setUser(prev => ({ ...prev, name: e.target.value }));
      };
    
      return (
        <input
          value={user.name}
          onChange={handleNameChange}
          placeholder="Имя"
        />
      );
    }
    

    Ленивая инициализация

    Если начальное состояние вычисляется дорого (например, парсинг данных из localStorage), передай функцию — она выполнится только при первом рендере:

    // Эта функция вызовется только один раз при монтировании
    const [data, setData] = useState(() => {
      const saved = localStorage.getItem('userData');
      return saved ? JSON.parse(saved) : { name: '', theme: 'light' };
    });
    

    Хук useEffect

    useEffect используется для выполнения побочных эффектов: запросов к API, подписок на события, работы с DOM, таймеров. Он принимает функцию-эффект и массив зависимостей.

    import { useState, useEffect } from 'react';
    
    function UserProfile({ userId }) {
      const [user, setUser] = useState(null);
      const [loading, setLoading] = useState(true);
    
      useEffect(() => {
        // этот код запустится после каждого рендера, где изменился userId
        setLoading(true);
    
        fetch(`/api/users/${userId}`)
          .then(res => res.json())
          .then(data => {
            setUser(data);
            setLoading(false);
          });
      }, [userId]); // зависимость — userId
    
      if (loading) return <p>Загрузка...</p>;
      return <h2>{user?.name}</h2>;
    }
    

    Массив зависимостей

    Поведение useEffect полностью определяется вторым аргументом:

    // Запускается после каждого рендера
    useEffect(() => {
      console.log('рендер');
    });
    
    // Запускается только один раз при монтировании
    useEffect(() => {
      console.log('компонент смонтирован');
    }, []);
    
    // Запускается при монтировании и при изменении value
    useEffect(() => {
      console.log('value изменился:', value);
    }, [value]);
    

    Функция очистки

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

    useEffect(() => {
      // подписываемся на событие resize
      const handleResize = () => {
        setWindowWidth(window.innerWidth);
      };
    
      window.addEventListener('resize', handleResize);
    
      // эта функция вызовется перед размонтированием или перед следующим эффектом
      return () => {
        window.removeEventListener('resize', handleResize);
      };
    }, []);
    

    Отмена запроса при размонтировании

    При работе с fetch важно обрабатывать ситуацию, когда компонент размонтируется до завершения запроса:

    useEffect(() => {
      // флаг для отслеживания актуальности компонента
      let isMounted = true;
    
      fetch(`/api/posts/${postId}`)
        .then(res => res.json())
        .then(data => {
          // обновляем состояние только если компонент ещё жив
          if (isMounted) {
            setPosts(data);
          }
        });
    
      return () => {
        isMounted = false;
      };
    }, [postId]);
    

    Совместное использование useState и useEffect

    Пример реального компонента поиска с дебаунсом:

    function SearchResults() {
      const [query, setQuery] = useState('');
      const [results, setResults] = useState([]);
      const [isSearching, setIsSearching] = useState(false);
    
      useEffect(() => {
        if (!query.trim()) {
          setResults([]);
          return;
        }
    
        setIsSearching(true);
    
        // откладываем запрос на 500мс после последнего ввода
        const timer = setTimeout(async () => {
          const res = await fetch(`/api/search?q=${query}`);
          const data = await res.json();
          setResults(data.items);
          setIsSearching(false);
        }, 500);
    
        // отменяем таймер если query изменился раньше
        return () => clearTimeout(timer);
      }, [query]);
    
      return (
        <div>
          <input
            value={query}
            onChange={e => setQuery(e.target.value)}
            placeholder="Поиск..."
          />
          {isSearching && <p>Ищем...</p>}
          <ul>
            {results.map(item => (
              <li key={item.id}>{item.title}</li>
            ))}
          </ul>
        </div>
      );
    }
    

    Частые ошибки

    1. Бесконечный цикл в useEffect

    Если внутри эффекта обновлять состояние, которое указано в зависимостях — получится бесконечный цикл:

    // Плохо: data в зависимостях, а внутри мы её обновляем
    useEffect(() => {
      setData([...data, newItem]); // вызовет перерендер → эффект запустится снова
    }, [data]);
    
    // Хорошо: используем функцию-обновитель, убираем data из зависимостей
    useEffect(() => {
      setData(prev => [...prev, newItem]);
    }, [newItem]);
    

    2. Пропуск зависимостей

    Никогда не игнорируй предупреждения ESLint о зависимостях useEffect. Пропуск переменной в массиве зависимостей приводит к работе с устаревшими данными (stale closure).

    3. Прямая мутация состояния

    // Плохо: мутируем массив напрямую — React не увидит изменение
    const [items, setItems] = useState([1, 2, 3]);
    items.push(4); // React не вызовет перерендер
    setItems(items);
    
    // Хорошо: создаём новый массив
    setItems(prev => [...prev, 4]);
    

    4. Асинхронная функция в useEffect

    useEffect не может принимать async-функцию напрямую. Объяви её внутри:

    // Плохо
    useEffect(async () => { ... }, []);
    
    // Хорошо
    useEffect(() => {
      const fetchData = async () => {
        const data = await getData();
        setState(data);
      };
      fetchData();
    }, []);
    

    Заключение

    useState и useEffect — фундамент работы с React-компонентами. useState управляет локальными данными компонента и перерисовкой, useEffect синхронизирует компонент с внешним миром. Ключевые правила: всегда указывай зависимости честно, не мутируй состояние напрямую и не забывай про функцию очистки. Освоив эти два хука на практике, ты будешь готов к изучению более сложных — useReducer, useContext и useCallback.

    Иконка глаза7

    Комментарии

    0

    Постройте личный план изучения JavaScript с нуля - основы языка и практика для начинающих до уровня Middle — бесплатно!

    JavaScript с нуля - основы языка и практика для начинающих — часть карты развития Frontend, Backend, Mobile

    • step100+ шагов развития
    • lessons30 бесплатных лекций
    • lessons300 бонусных рублей на счет

    Бесплатные лекции

    Лучшие курсы по теме

    изображение курса

    Продвинутый JavaScript

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    TypeScript с нуля

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    Neovim

    Антон Ларичев
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее

    Похожие статьи

    Картинка поста Async await в JavaScript: подробный разбор с примерами
    Иконка аватараАнтон
    Иконка календаря19 июля 2026
    JavaScriptasync/awaitасинхронность+ 3middleИконка уровня middle

    Async await в JavaScript: подробный разбор с примерами

    Разбираем async/await в JavaScript с нуля: как работает синтаксис, обработка ошибок через try/catch, параллельные запросы с Promise.all и частые ошибки

    Иконка чипа0
    Иконка глаза254
    Иконка комментариев0
    Картинка поста Как установить Node.js на Windows: пошаговая инструкция
    Иконка аватараАнтон
    Иконка календаря17 июля 2026
    Node.jsWindowsnpm+ 2juniorИконка уровня junior

    Как установить Node.js на Windows: пошаговая инструкция

    Как установить Node.js на Windows — пошаговая инструкция через официальный установщик и nvm-windows с разбором частых ошибок.

    Иконка чипа0
    Иконка глаза232
    Иконка комментариев0
    Картинка поста Серверные компоненты Next.js: внутреннее устройство
    Иконка аватараАнтон
    Иконка календаря09 июля 2026
    Next.jsReactServer Components+ 2middleИконка уровня middle

    Серверные компоненты Next.js: внутреннее устройство

    Серверные компоненты Next.js выполняются только на сервере, уменьшают JS-бандл и передают данные через RSC Payload. Разбираем, как это работает изнутри.

    Иконка чипа0
    Иконка глаза187
    Иконка комментариев0
    Иконка чипа0