Гид по технологиям

useState в React: руководство по функциональным компонентам

9 min read React Обновлено 18 Nov 2025
useState в React — полное руководство
useState в React — полное руководство

Логотип React на тёмном фоне

Быстрые ссылки

  • Классический подход

  • Преобразование в функциональный компонент

  • Устройство хука useState

  • Обновление состояния

  • Начальные значения

  • Использование нескольких значений состояния

  • Когда useState не подходит

  • Альтернативы и рекомендации

  • Руководство по миграции из классов в функции

  • Заключение


Классический подход

В классических React-компонентах состояние хранится в свойстве state экземпляра компонента. Для изменения состояния используется метод setState(), который частично объединяет (shallow merge) новое значение с текущим состоянием и вызывает перерендер.

Пример классового компонента с простым счётчиком:

class MyComponent extends React.Component {
  state = { value: 1 };

  updateState = () => this.setState({ value: (this.state.value + 1) });

  render() {
    return (
      

{this.state.value}

); } }

В этом примере текст всегда отображает текущее число из состояния. Нажатие на кнопку увеличивает значение.

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

Преобразование в функциональный компонент

Функциональные компоненты по умолчанию были «без состояния» до появления хуков. Начиная с React 16.8 появилась возможность использовать состояние в функциях с помощью хука useState(). Это делает компоненты короче и читабельнее.

Эквивалент предыдущего компонента с useState:

import React, { useState } from "react";

const MyComponent = () => {
  const [value, setValue] = useState(1);

  return (
    

{value}

); };

Отличия и преимущества:

  • Меньше шаблона (нет class/constructor).
  • Явные имена переменных и функций-обновителей (по соглашению setX).
  • Легче комбинировать с другими хуками (useEffect, useReducer и т. д.).

Устройство хука useState

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

Ключевые моменты:

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

Пример с именованием:

const [value, setValue] = useState(1);

По соглашению функция называется setX, где X — имя переменной состояния.

1-строчное определение: useState создаёт локальную ссылку на значение, которая сохраняется между вызовами функции-компонента.

Обновление состояния

Функция-обновитель, возвращаемая useState, ведёт себя как обычная функция: вы вызываете её с новым значением или передаёте функцию, которая получит текущее состояние и вернёт следующее.

Примеры:

Прямое присвоение:

setValue(5);

Функциональный обновитель (рекомендуется при вычислениях от предыдущего состояния):

setValue(prev => prev + 1);

Почему это важно:

  • Если обновление зависит от предыдущего значения, нужно использовать функциональный паттерн, чтобы избежать ошибок со «сталыми» замыканиями и конкурентными обновлениями.
  • React может батчировать обновления и вызывать несколько обновителей подряд; функциональный стиль гарантирует последовательность вычислений.

Отличие от setState в классах:

const [value, setValue] = useState({ foo: "bar", test: { example: "demo" } });
setValue({ foo: "foobar" });
// Результат: { foo: "foobar" }

// В классе:
this.state = { foo: "bar", test: { example: "demo" } };
this.setState({ foo: "foobar" });
// Результат: { foo: "foobar", test: { example: "demo" } }

useState заменяет значение полностью. Если вам нужно обновить часть объекта, создайте новый объект с помощью spreаd-оператора или используйте useReducer.

Пример обновления поля объекта:

setUser(prevUser => ({ ...prevUser, username: "newName" }));

Проблемы и типичные ошибки:

  • Мутирование объекта состояния вместо создания нового объекта приведёт к тому, что React не заметит изменения.
  • Использование устаревших значений в обработчиках при отсутствии функционального обновителя.
  • Передача функции-обновителя вместо результата: setValue(toggle) — допустимо, если toggle — функция, принимающая предыдущее значение.

Начальные значения

useState принимает начальное значение в качестве аргумента. Это может быть примитив, объект или функция-инициализатор.

Прямое значение:

const [value, setValue] = useState(1);

Ленивая инициализация (функция вызывается только при монтировании):

const initialState = () => computeExpensiveValue();
const [value, setValue] = useState(initialState);

Если вы передаёте функцию-инициализатор (например, doSomethingExpensive), React вызовет её один раз при первой инициализации состояния. Это экономит ресурсы, потому что вычисление не выполняется на каждом рендере.

Важное примечание про режим Strict Mode в разработке:

  • В React Strict Mode (разработка) некоторые функции инициализации могут быть вызваны дважды, чтобы обнаружить побочные эффекты. Это влияет на функции-инициализаторы: они могут быть вызваны повторно в процессе проверки, поэтому начинать побочные эффекты из функции-инициализатора не рекомендуется.

Использование нескольких значений состояния

Есть два основных подхода:

  1. Одно состояние-объект (как в классах)
const [user, setUser] = useState({ id: 1, username: "foobar" });
setUser({ ...user, username: "example" });

Плюсы:

  • Похож на поведение классов.

Минусы:

  • Вам нужно вручную сливать свойства.
  • Обновления могут быть менее локализованы и сложнее тестировать.
  1. Несколько useState для отдельных частичных состояний (рекомендуется)
const [userId, setUserId] = useState(1);
const [username, setUsername] = useState("foobar");

Плюсы:

  • Явные «ведра» состояния для каждой логической единицы.
  • Проще оптимизировать локальные обновления и мемоизацию.

Минусы:

  • Больше строк кода, если значений много.

Выбор зависит от семантики: если поля тесно связаны и всегда обновляются вместе, храните их в одном объекте; если поля независимы — используйте отдельные useState.

Когда useState не подходит

useState хорош для локального примитивного состояния, флагов, форм и отдельных полей. Но есть случаи, когда лучше другие инструменты:

  • Сложная логика обновления с множественными ветвлениями и побочными эффектами — рассмотрите useReducer.
  • Глобальное состояние, используемое многими компонентами — рассмотрите Context + useReducer, или внешние решения (Redux, Zustand, Recoil).
  • Частые изменения большого глубоко вложенного объекта — useState с глубокими копиями приводит к ошибкам и низкой производительности; подумайте о нормализации состояния или useReducer.

Альтернативы и сочетания

  • useReducer: полезен, когда у вас есть сложное управление состоянием с бизнес-логикой и множеством типов действий. Reducer позволяет централизовать переходы состояний и облегчает тестирование.

  • Context: предоставляет доступ к состоянию от верхнего уровня приложения без проброса пропсов; часто комбинируется с useReducer для управления глобальным состоянием.

  • Лёгкие сторонние решения (Zustand, Jotai, Recoil): подходят, если нужен минималистичный глобальный стор.

Комбинации:

  • Используйте useState для локального UI-состояния (открыт/закрыт модал) и useReducer/Context для доменной логики или данных, которые нужны в нескольких местах.

Ментальные модели и эвристики

  • «Ведро на каждую причину изменения»: держите отдельное состояние для независимых частей интерфейса.
  • «Компоновка вместо единого объекта»: если части состояния естественно отделимы, разделите их — это упрощает логику.
  • «Функция-обновитель против прямого значения»: всегда используйте функциональный обновитель, если новое значение вычисляется из старого.

Короткая шпаргалка:

  • Флаги, счётчики, ввод — useState.
  • Множественные действия и сложные переходы — useReducer.
  • Данные приложения, которыми делятся многие компоненты — Context/Store.

Руководство по миграции из классов в функциональные компоненты

Ниже — пошаговый план для разработчиков, которые переводят компонент на хуки.

Шаги миграции:

  1. Замените class на const MyComponent = () => {} и перенесите JSX из render.
  2. Переместите начальное state в useState: const [state, setState] = useState(initialState).
  3. Перепишите методы класса как функции внутри компонента; замените this на локальные переменные/замыкания.
  4. Замените this.setState на setState; при частичном обновлении объекта используйте spreаd или разбейте состояние на несколько useState.
  5. Перенесите побочные эффекты (lifecycle) в useEffect с корректными зависимостями.
  6. Проверьте обработчики и привязку контекста — этого теперь не требуется.
  7. Покройте компонент тестами: проверяйте поведение при кликах, вводе, инициализации состояния.

Критерии приёмки:

  • Вся функциональность сохраняется (UI и поведение).
  • Нет подвешанных ссылок на this.
  • Тесты, которые проходили ранее, проходят и для нового компонента.
  • Нагрузка и производительность сравнимы с исходным компонентом.

Пример playbook для команды:

  • Подготовка: написать unit-тесты или E2E для текущего поведения.
  • Миграция: перенос кода и замена lifecycle на useEffect.
  • Рефакторинг: выделение логики в кастомные хуки при необходимости.
  • Проверка: просмотреть профили CPU/рамки памяти при сложных компонентов.

Тест-кейсы и приёмка

Общие тесты при переходе на useState:

  • Инициализация: компонент показывает исходное значение состояния.
  • Обновление: клик/ввод меняет состояние и UI отражает это.
  • Параллельные обновления: несколько быстрых кликов корректно увеличивают счётчик.
  • Ленивая инициализация: тяжёлая функция инициализируется один раз при монтировании (в продакшне).

Примеры тестов (Jest + React Testing Library):

  • Проверка клика инкремента:

    • Рендер компонента — убедиться, что число 1 отображается.
    • Вызывать клик на кнопке — ожидать 2.
  • Проверка функционального обновителя, при множественных вызовах:

    • Вызывать setValue(prev => prev + 1) несколько раз — итог совпадает с суммой.

Edge-case gallery (частые грабли)

  • Мутация объекта состояния: изменение свойства объекта без создания нового объекта — React может не обнаружить изменение.
  • Неправильный порядок хуков: условный вызов useState приведёт к ошибке «Hooks must be called in the same order».
  • Замыкания: обработчики, использующие устаревшие значения состояния, если не применялся функциональный обновитель.
  • Дублирование инициализаторов в Strict Mode: не рассчитывайте на побочные эффекты внутри функции-инициализатора.

Производительность и оптимизация

  • Разделяйте состояние на логические куски, чтобы предотвратить лишние перерендеры дочерних компонентов.
  • Используйте React.memo, useMemo и useCallback для мемоизации значимых вычислений и колбеков.
  • Если у вас сложная логика состояния с множеством действий, useReducer упрощает профилирование и оптимизации.

Безопасность и приватность

  • useState сам по себе не влияет на безопасность, но будьте осторожны при хранении чувствительных данных (токенов, персональных данных) в состоянии браузера.
  • Для длительного хранения и обмена между сессиями используйте безопасные механизмы (например, HTTP-only куки для токенов), а не localStorage, если необходимо соблюдать рекомендации по безопасности.
  • Для соответствия требованиям GDPR убедитесь, что личные данные обрабатываются и сохраняются в соответствии с политиками компании и законодательства.

Совместимость и заметки по версиям

  • Хуки доступны, начиная с React 16.8.
  • Для новых возможностей (параллельный режим, автоматическое батчирование, Strict Mode поведение) следите за изменениями в релиз-нотах React (React 18 и выше ввели поведение автоматического батчинга для промисов/таймеров).
  • При поддержке старых приложений, которые используют классы, комбинирование компонентов классов и функций в одном приложении допустимо.

Decision flow: выбрать useState или useReducer

flowchart TD
  A[Нужна ли локальная простая логика?] -->|Да| B[useState]
  A -->|Нет| C[Сложная логика / много действий]
  C --> D[useReducer]
  B --> E[Много независимых состояний?]
  E -->|Да| F[Несколько useState]
  E -->|Нет| G[Один useState с объектом]

Чек-лист для разработчика, тимлида и QA

  • Разработчик:

    • Замена this и методов на локальные функции.
    • Использование functional updater при зависимостях от предыдущего состояния.
    • Предотвращение мутаций. Создание новых объектов при обновлении.
  • Тимлид:

    • Архитектура состояния согласована (локальное vs глобальное).
    • План миграции и rollback есть.
  • QA:

    • Тесты cover случаев инициализации и обновления.
    • Проверка в режиме Strict Mode и в продакшн сборке.

Примеры полезных сниппетов

Тоггл-логика, повторно используемая:

const useToggle = (initial = false) => {
  const [state, setState] = useState(initial);
  const toggle = useCallback(() => setState(prev => !prev), []);
  return [state, toggle];
};

Ленивая инициализация с защитой от побочных эффектов:

const computeExpensiveValue = () => {
  // тяжёлая синхронная операция
  return heavyComputation();
};
const [value, setValue] = useState(() => computeExpensiveValue());

Короткое объявление для команды (100–200 слов)

useState — базовый хук для управления локальным состоянием в функциональных компонентах React. Он возвращает текущее значение и функцию для его обновления; обновитель заменяет значение полностью, поэтому для частичных обновлений объектов используйте spreаd или функциональный обновитель. Для сложной логики лучше применять useReducer или сочетание Context + useReducer. При миграции классов на функции переносите state в useState, lifecycle-методы в useEffect и проверяйте поведение в режиме Strict Mode и через тесты. Используйте ленивую инициализацию для дорогих вычислений и функциональные обновители, чтобы избежать ошибок со старыми замыканиями.

Заключение

useState делает функциональные компоненты полными участниками парадигмы React, сохраняя простоту и читаемость. Выбирайте подход (несколько useState vs объект vs useReducer) согласно семантике данных и сложности логики. Помните про иммутабельность, функциональные обновители и ленивую инициализацию — это основные приёмы для надёжных и производительных React-компонентов.


Ключевые моменты:

  • useState подходит для локального состояния и простых сценариев.
  • Для сложной логики используйте useReducer.
  • Всегда избегайте мутаций и используйте функциональные обновители при зависимости от предыдущего значения.
  • Ленивую инициализацию используйте для тяжёлых вычислений.
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Unity Lights: циферблат Apple Watch для Black History Month
Apple Watch

Unity Lights: циферблат Apple Watch для Black History Month

Обновление видеодрайвера для Rainbow Six Siege
Windows

Обновление видеодрайвера для Rainbow Six Siege

Ограничение частоты запросов в ASP.NET Core
Backend

Ограничение частоты запросов в ASP.NET Core

Исправление лагов Android: TRIM и LagFix
Mobile

Исправление лагов Android: TRIM и LagFix

Семафоры в Bash: что это и как реализовать
Bash

Семафоры в Bash: что это и как реализовать

Что делать при перегреве PS5
Гайды

Что делать при перегреве PS5