Локальное vs глобальное состояние
Локальное состояние живёт внутри компонента и недоступно снаружи; глобальное — доступно любому компоненту приложения через контекст или внешнее хранилище.
Зачем нужно
Одна из частых архитектурных ошибок — вынесение всего в глобальный store (Redux, Zustand) без необходимости. Это усложняет код, создаёт лишние зависимости и затрудняет переиспользование компонентов. Правило простое: начинайте с локального state, поднимайте выше только когда данные нужны нескольким компонентам, и выносите в глобальный store только по-настоящему глобальные данные.
Где используется
- Локальное: открыт/закрыт аккордеон, текущее значение формы, hover-состояние
- Поднятое (lifted): данные, которые нужны двум соседним компонентам
- Контекст: тема, язык, данные авторизованного пользователя
- Глобальный store (Redux/Zustand): корзина покупок, сложная серверная коллекция, состояние с undo/redo
Иерархия состояний
Глобальный store (Redux/Zustand/Jotai)
├── Данные, нужные всему приложению
├── Сложная логика с side effects
└── Состояние, требующее middleware/DevTools
Context API
├── Тема оформления
├── Текущий пользователь
└── Локализация
Lifted State (поднятое состояние)
├── Данные, нужные двум соседним компонентам
└── Хранится в ближайшем общем родителе
Local State (useState/useReducer)
├── UI-состояние компонента (открыт/закрыт)
├── Значения формы
└── Данные, не нужные другим компонентам
Когда использовать что
// ЛОКАЛЬНОЕ — правильно: состояние принадлежит компоненту
function Accordion({ title, children }) {
const [isOpen, setIsOpen] = useState(false); // только этот компонент знает
return (
<div>
<button onClick={ => setIsOpen(prev => !prev)}>{title}</button>
{isOpen && <div>{children}</div>}
</div>
);
}
// ПОДНЯТОЕ — правильно: данные нужны двум компонентам
function SearchPage() {
const [query, setQuery] = useState(''); // поднято в общего родителя
return (
<>
<SearchInput value={query} onChange={setQuery} />
<SearchResults query={query} /> {/* нужен тот же query */}
</>
);
}
// ГЛОБАЛЬНОЕ (Context) — правильно: тема нужна везде
const ThemeContext = createContext('light');
// ГЛОБАЛЬНОЕ (Zustand) — правильно: корзина доступна с любой страницы
const useCartStore = create((set, get) => ({
items: ,
addItem: (item) => set(state => ({ items: [...state.items, item] })),
total: => get.items.reduce((sum, item) => sum + item.price, 0),
}));
Признаки неправильного выбора
Слишком много в store:
✗ Redux хранит значение инпута поиска
✗ Redux хранит isModalOpen
→ Перенести в useState
Слишком много в local state:
✗ useState в компоненте А, useState в компоненте Б — дубль одних данных
✗ Постоянный props drilling через 5 уровней
→ Поднять state или использовать Context
Частые ошибки
- Глобализация всего — помещают каждый флаг в Redux/Zustand; от этого страдает производительность (лишние ре-рендеры) и читаемость.
- Локализация того, что должно быть глобальным — корзина в
useStateодного компонента; при переходе на другую страницу данные теряются. - Context для часто меняющихся данных — позиция курсора в Context вызывает ре-рендер всех подписчиков при каждом движении мыши.
Связанные темы
- _MOC SPA
- State -- внутреннее состояние
- Context API -- обзор
- Props Drilling -- проблема
- Redux -- концепции и архитектура