Состояние — это сердце любого приложения. От того, насколько грамотно вы им управляете, зависит, будет ли ваш код понятным, масштабируемым и быстрым. Если вы когда-нибудь ловили себя на мысли «почему эта переменная изменилась?» или «кто обновил этот объект?», значит, пора навести порядок в управлении состоянием. В этой статье я расскажу, как это сделать без боли и лишних библиотек.
Что такое состояние и почему оно болит
Состояние — это любые данные, которые могут меняться во времени и влияют на отображение интерфейса. Например, счётчик кликов, список задач, флаг загрузки, авторизованный пользователь. В маленьком приложении достаточно одной переменной, но с ростом сложности появляются проблемы:
- Данные дублируются в разных местах.
- Изменения происходят в неожиданных местах.
- Компоненты зависят от глобальных переменных.
- Становится трудно отлаживать и тестировать.
Всё это называется «ад состояний». Чтобы его избежать, нужно выбрать правильный подход и следовать простым правилам.
Простые случаи: локальное состояние
Не нужно тащить 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.
- Разделяйте состояние по доменам. Не пихайте всё в один огромный объект. Группируйте по смыслу: пользователь, товары, настройки.
- Используйте селекторы. Выносите вычисляемые данные в отдельные функции, чтобы не дублировать логику.
Пример использования селектора с простым стором:
// Селектор для получения общего количества товаров
function selectTotalQuantity(state) {
return state.cart.items.reduce((sum, item) => sum + item.quantity, 0);
}
// Использование
const total = selectTotalQuantity(store.getState());
Что в итоге
Управление состоянием — это не про выбор библиотеки, а про дисциплину и понимание потребностей приложения. Начните с локального состояния, переходите к централизованному только когда почувствуете боль. Следуйте принципам единого источника правды и неизменяемости. И не бойтесь писать свои простые решения — иногда они лучше тяжёлых фреймворков.
Помните: хорошее состояние — это предсказуемое состояние. Удачи в рефакторинге!



