Состояние — это сердце любого приложения. От того, насколько грамотно вы им управляете, зависит, будет ли ваш код понятным, масштабируемым и быстрым. Если вы когда-нибудь ловили себя на мысли «почему эта переменная изменилась?» или «кто обновил этот объект?», значит, пора навести порядок в управлении состоянием. В этой статье я расскажу, как это сделать без боли и лишних библиотек.

Digital-студия WNDER

Что такое состояние и почему оно болит

Состояние — это любые данные, которые могут меняться во времени и влияют на отображение интерфейса. Например, счётчик кликов, список задач, флаг загрузки, авторизованный пользователь. В маленьком приложении достаточно одной переменной, но с ростом сложности появляются проблемы:

  • Данные дублируются в разных местах.
  • Изменения происходят в неожиданных местах.
  • Компоненты зависят от глобальных переменных.
  • Становится трудно отлаживать и тестировать.

Всё это называется «ад состояний». Чтобы его избежать, нужно выбрать правильный подход и следовать простым правилам.

Простые случаи: локальное состояние

Не нужно тащить Redux в приложение из трёх кнопок. Для начала используйте локальное состояние — переменные внутри компонента или функции. В современном JavaScript это делается через let и const, а в React — через useState.

// Простой счётчик на чистом JS
let count = 0;
const counterElement = document.getElementById('counter');

document.getElementById('increment').addEventListener('click', () => {
  count += 1;
  counterElement.textContent = count;
});

Пока состояние живёт в одном месте и не нужно другим частям приложения — этого достаточно. Главное правило: не делайте состояние глобальным без необходимости.

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

Централизованное состояние: когда без него не обойтись

Когда несколько компонентов должны реагировать на одни и те же данные (например, корзина покупок и счётчик в шапке), локальное состояние становится неудобным. Приходится «прокидывать» пропсы через полдерева или использовать глобальные переменные. Тут на помощь приходят паттерны централизованного состояния.

Самый простой вариант — хранилище (store) с подпиской. Реализуем его на чистом JavaScript:

// Простой store с подпиской
function createStore(initialState) {
  let state = initialState;
  const listeners = [];

  return {
    getState: () => state,
    setState: (newState) => {
      state = { ...state, ...newState };
      listeners.forEach(listener => listener(state));
    },
    subscribe: (listener) => {
      listeners.push(listener);
      return () => {
        const index = listeners.indexOf(listener);
        if (index > -1) listeners.splice(index, 1);
      };
    }
  };
}

// Использование
const store = createStore({ count: 0 });
store.subscribe((state) => console.log('State updated:', state));
store.setState({ count: 1 }); // выведет: State updated: { count: 1 }

Такой подход уже даёт единый источник правды и предсказуемые обновления. Но для больших приложений нужны более мощные инструменты.

Популярные библиотеки: сравнение

Выбор библиотеки зависит от сложности проекта и ваших предпочтений. Вот таблица с основными характеристиками:

Библиотека Подход Сложность Когда использовать
Redux Единый store, чистые редьюсеры Высокая Крупные приложения с множеством состояний
MobX Реактивные observables Средняя Проекты, где важна простота и автоматическая реактивность
Zustand Минималистичный store на хуках Низкая Небольшие и средние приложения, быстрый старт
React Context Встроенный механизм React Низкая Передача данных глубоко в дерево без пропсов

Не гонитесь за модой. Если у вас небольшое приложение, возможно, хватит React Context или даже простого стора, который мы написали выше.

Практические советы для эффективного управления

Вот несколько проверенных приёмов, которые помогут избежать хаоса:

  • Единый источник правды. Все данные должны храниться в одном месте (store), а компоненты только читают их и отправляют действия на изменение.
  • Неизменяемость (immutability). Не мутируйте состояние напрямую. Создавайте новые объекты при обновлении. Это упрощает отслеживание изменений и работу с React.
  • Разделяйте состояние по доменам. Не пихайте всё в один огромный объект. Группируйте по смыслу: пользователь, товары, настройки.
  • Используйте селекторы. Выносите вычисляемые данные в отдельные функции, чтобы не дублировать логику.
Лайфхак: Для отладки состояния подключите Redux DevTools (даже если не используете Redux) — многие библиотеки поддерживают этот интерфейс. Это как рентген для ваших данных.

Пример использования селектора с простым стором:

// Селектор для получения общего количества товаров
function selectTotalQuantity(state) {
  return state.cart.items.reduce((sum, item) => sum + item.quantity, 0);
}

// Использование
const total = selectTotalQuantity(store.getState());

Что в итоге

Управление состоянием — это не про выбор библиотеки, а про дисциплину и понимание потребностей приложения. Начните с локального состояния, переходите к централизованному только когда почувствуете боль. Следуйте принципам единого источника правды и неизменяемости. И не бойтесь писать свои простые решения — иногда они лучше тяжёлых фреймворков.

Помните: хорошее состояние — это предсказуемое состояние. Удачи в рефакторинге!

Студия WNDER