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.

    Иконка глаза1 285

    Комментарии

    0

    Постройте личный план изучения Feature-Sliced Design до уровня Middle — бесплатно!

    Feature-Sliced Design — часть карты развития Frontend

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

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

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

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

    Angular

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

    Vue 3 и Pinia

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

    Nuxt

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

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

    Картинка поста OpenCode: пользовательские провайдеры недоступны — что делать
    Иконка аватараАнтон
    Иконка календаря26 октября 2026
    AI-инструментыapiразработкаjuniorИконка уровня junior

    OpenCode: пользовательские провайдеры недоступны — что делать

    Ошибка «Пользовательские провайдеры недоступны на этом сервере» (в английском интерфейсе — «Custom providers are unavailable on this server») появляется в OpenCode Desktop и веб-интерфейсе OpenCode, когда вы сохраняете своего провайдера через «Настройки → Провайдеры → Пользовательский провайдер». Это ошибка самого OpenCode, а не вашего ключа, сервера или региона: в версиях 2.0.x форма сохранения не подключена к серверу и выдаёт эту ошибку при любых введённых данных. VPN и смена ключа не помогут. Решение — описать провайдера в файле `opencode.json`: так он работает и в Desktop, и в терминальной версии. Ниже — что именно сломано, готовые конфиги для Claude, GPT и Qwen и что делать, если модели не появились.

    Иконка чипа0
    Иконка глаза171
    Иконка комментариев0
    Картинка поста Как тратить меньше токенов в Claude Code
    Иконка аватараАнтон
    Иконка календаря25 октября 2026
    Claude CodeAnthropicAI-инструментыmiddleИконка уровня middle

    Как тратить меньше токенов в Claude Code

    Чтобы Claude Code тратил меньше токенов, сократите контекст, который он пересылает с каждым запросом, и не ломайте кэш посреди задачи. На практике это пять привычек: `/clear` между несвязанными задачами, `/compact` в паузах, а не посреди работы, Sonnet вместо Opus для повседневного кода, выбор модели в начале сессии, а не на середине, и короткий `CLAUDE.md`. Дороже всего в счёте обходится не длина ваших вопросов, а новый контекст и пересборка кэша — именно их эти привычки и сокращают.

    Иконка чипа0
    Иконка глаза175
    Иконка комментариев0
    Картинка поста Qwen 3.8 Max для кода: как подключить из России
    Иконка аватараАнтон
    Иконка календаря24 октября 2026
    AI-инструментыapiразработкаjuniorИконка уровня junior

    Qwen 3.8 Max для кода: как подключить из России

    Qwen 3.8 Max — флагманская модель Alibaba с контекстом в 1 млн токенов и ответом до 131 тыс. токенов, которую Alibaba позиционирует прежде всего для программирования. Работать с ней из России проще всего в Qwen Code или OpenCode через ключ AI для кода от PurpleSchool: оплата в рублях, без VPN и без аккаунта Alibaba Cloud. Модель подключается по протоколу OpenAI Responses API — ниже готовые конфиги для обоих клиентов, характеристики модели и частые ошибки.

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