Менеджеры состояний и контекста
Выбор между этими инструментами зависит от масштаба проекта, опыта команды и требований к производительности.
| Характеристика | React Context | Zustand | Redux (Toolkit) | MobX |
|---|---|---|---|---|
| Лучшее применение | Темы, авторизация | Малые и средние приложения | Сложные корпоративные системы | Часто меняющиеся данные |
| Количество кода | Мало | Очень мало | Много (даже с RTK) | Мало |
| Порог вхождения | Низкий | Низкий | Высокий | Средний |
| Перерендеры | Часто (без оптимизации) | Точечно (оптимизировано) | Точечно (оптимизировано) | Точечно (автоматически) |
| Поток данных | Встроенный Provider | Простые хуки | Однонаправленный (Actions) | Объекты (Observables) |
Zustand сейчас — самый популярный выбор для новых проектов, потому что он очень легкий и почти не требует шаблонного кода.
- Плюсы: Максимально простое API на основе хуков, не нужны провайдеры, отличная производительность "из коробки".
- Минусы: В нем меньше строгой структуры, чем в Redux, что может быть важно для огромных проектов с десятками разработчиков.
- Когда использовать: Почти в любом новом проекте, где нужен глобальный стейт без головной боли.
import { create } from 'zustand'
const useCounter = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}))function Counter() {
const { count, increment, decrement } = useCounter()
return (
<div>
<h1>{count}</h1>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
)
}Проверенный временем инструмент, который навязывает строгую и предсказуемую архитектуру.
- Плюсы: Мощные инструменты отладки (Redux DevTools), огромное сообщество, предсказуемость благодаря функциональному подходу.
- Минусы: Избыточен для маленьких проектов, требует много кода даже при использовании современного Redux Toolkit.
- Когда использовать: Большие корпоративные приложения со сложной логикой или в командах, где важны жесткие правила написания кода.
import { createSlice } from '@reduxjs/toolkit'
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1 }, // Можно "мутировать" благодаря Immer
},
})
export const { increment } = counterSlice.actions
export default counterSlice.reducerimport { useSelector, useDispatch } from 'react-redux'
function Counter() {
const count = useSelector((state) => state.counter.value)
const dispatch = useDispatch()
return <button onClick={() => dispatch(increment())}>{count}</button>
}Это часть самого React. Технически это не библиотека управления состоянием, а способ передачи данных через дерево компонентов без "прокидывания" пропсов.
- Плюсы: Не увеличивает размер бандла, идеально для статических или редко меняющихся данных.
- Минусы: Проблемы с производительностью при частых обновлениях, так как любое изменение стейта заставляет перерендериваться всех потребителей этого контекста.
- Когда использовать: Для редко меняющихся данных: тема (темная/светлая), текущий язык интерфейса, данные авторизованного пользователя.
const CounterContext = createContext()
function CounterProvider({ children }) {
const [count, setCount] = useState(0)
return (
<CounterContext.Provider value={{ count, setCount }}>
{children}
</CounterContext.Provider>
)
}function Counter() {
const { count, setCount } = useContext(CounterContext)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}MobX работает на основе "наблюдаемых" (observables) данных, на которые компоненты подписываются автоматически.
- Плюсы: Минимум кода, работа в объектно-ориентированном стиле. Очень быстрая реакция на изменения.
- Минусы: Сложнее отлаживать, так как обновления происходят "магически" под капотом; менее популярен в экосистеме React, чем Redux или Zustand.
- Когда использовать: В приложениях с очень динамичными, взаимосвязанными данными или если вам привычнее мутировать объекты напрямую, а не работать с иммутабельностью.
import { makeAutoObservable } from "mobx";
class CounterStore {
count = 0;
constructor() {
// Делает всё в классе реактивным
makeAutoObservable(this);
}
// Просто меняем значение напрямую
increment() {
this.count += 1;
}
decrement() {
this.count -= 1;
}
}
export const counterStore = new CounterStore();import React from "react";
import { observer } from "mobx-react-lite";
import { counterStore } from "./counterStore";
// observer следит за тем, какие данные из store используются в render
const Counter = observer(() => {
return (
<div>
<h1>{counterStore.count}</h1>
<button onClick={() => counterStore.increment()}>+</button>
<button onClick={() => counterStore.decrement()}>-</button>
</div>
);
});
export default Counter;