Что такое Zustand и чем отличается от Redux?
Zustand: основы
Zustand (нем. «состояние») — небольшая (~1 КБ) библиотека управления состоянием, разработанная командой Pmndrs (авторы Jotai, React Spring). Она предоставляет глобальное хранилище через обычный хук и не накладывает жёстких архитектурных ограничений.
Ключевые особенности Zustand
- Минимальный API — хранилище создаётся одной функцией
create, а данные читаются через хук с селектором. - Без Provider — состояние живёт вне дерева компонентов, компоненты подписываются напрямую.
- Точечные ре-рендеры — компонент перерисовывается только при изменении выбранного им среза состояния.
- Мутабельный стиль через Immer — опционально, через middleware
immer. - DevTools — подключается через middleware
devtools.
Ключевые особенности Redux
- Строгий однонаправленный поток — action → reducer → store → view.
- Иммутабельность обязательна (или через Redux Toolkit + Immer).
- Большой boilerplate в классическом варианте; Redux Toolkit сокращает его, но архитектура остаётся.
- Мощная экосистема — Redux Saga, Redux Thunk, RTK Query, расширенный DevTools.
- Provider обязателен — всё приложение оборачивается в
<Provider store={store}>.
Сравнение по ключевым параметрам
| Критерий | Zustand | Redux (RTK) |
|---|---|---|
| Размер бандла | ~1 КБ | ~12–40 КБ |
| Boilerplate | Минимальный | Умеренный (RTK) |
| Архитектура | Свободная | Flux / строгая |
| Async | Напрямую в actions | Thunk / Saga / RTK Query |
| DevTools | Middleware | Встроено |
| TypeScript | Отличная поддержка | Отличная поддержка |
Когда выбирать Zustand
Zustand оптимален для небольших и средних приложений, прототипов, когда нужно быстро поднять глобальное состояние без лишней инфраструктуры. Хорошо подходит для UI-состояния: модалки, темы, фильтры.
Когда выбирать Redux
Redux (особенно RTK) оправдан в крупных командных проектах, где важна предсказуемость, трассируемость каждого изменения через DevTools и зрелая экосистема (RTK Query для кеширования API, Saga для сложных side-effect сценариев).
Пример
См. примеры кода ниже.
Итог
Zustand — это «меньше кода, быстрее старт». Redux — «больше структуры, лучше масштабирование в большой команде». Выбор зависит от размера проекта, команды и требований к предсказуемости архитектуры.
Что хочет услышать интервьюер
Кандидат понимает, что Zustand — альтернатива Redux, а не замена Context API
Знает об отсутствии необходимости в Provider и dispatcher в Zustand
Может объяснить, что в Redux есть строгий однонаправленный поток (action → reducer → store)
Осознаёт разницу в объёме boilerplate и размере бандла
Понимает, когда уместен каждый инструмент (масштаб проекта, команда, экосистема)
Пример: Zustand: создание хранилища и использование в компоненте
import { create } from 'zustand';
interface CounterState {
count: number;
increment: () => void;
decrement: () => void;
reset: () => void;
}
// Создаём хранилище — всё в одном месте, без actions/reducers
export const useCounterStore = create<CounterState>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}));
// Использование в компоненте — подписка только на нужный срез
function Counter() {
// Компонент перерисуется только при изменении count
const count = useCounterStore((state) => state.count);
const { increment, decrement, reset } = useCounterStore();
return (
<div>
<p>Счётчик: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Сбросить</button>
</div>
);
}
Пример: Redux Toolkit: эквивалентный пример
import { createSlice, configureStore, PayloadAction } from '@reduxjs/toolkit';
import { Provider, useDispatch, useSelector } from 'react-redux';
// Slice объединяет actions и reducer
const counterSlice = createSlice({
name: 'counter',
initialState: { count: 0 },
reducers: {
increment: (state) => { state.count += 1; }, // Immer под капотом
decrement: (state) => { state.count -= 1; },
reset: (state) => { state.count = 0; },
},
});
export const { increment, decrement, reset } = counterSlice.actions;
export const store = configureStore({
reducer: { counter: counterSlice.reducer },
});
type RootState = ReturnType<typeof store.getState>;
// Компонент требует Provider выше по дереву
function Counter() {
const count = useSelector((state: RootState) => state.counter.count);
const dispatch = useDispatch();
return (
<div>
<p>Счётчик: {count}</p>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
<button onClick={() => dispatch(reset())}>Сбросить</button>
</div>
);
}
// Обязательная обёртка в корне приложения
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
);
}
Пример: Zustand с middleware: devtools + persist
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
interface ThemeState {
theme: 'light' | 'dark';
toggleTheme: () => void;
}
// Оборачиваем в devtools и persist — состояние сохраняется в localStorage
export const useThemeStore = create<ThemeState>()(
devtools(
persist(
(set) => ({
theme: 'light',
toggleTheme: () =>
set(
(state) => ({ theme: state.theme === 'light' ? 'dark' : 'light' }),
false, // заменить, а не смёржить
'toggleTheme' // имя действия в DevTools
),
}),
{ name: 'theme-storage' } // ключ в localStorage
)
)
);
Типичные ошибки
Считают, что Zustand «лучше» Redux безоговорочно — не учитывают масштаб и требования проекта
Путают Zustand с Context API, не понимая, что Zustand решает проблему лишних ре-рендеров
Не знают, что Redux без Redux Toolkit действительно громоздкий, но RTK сильно упрощает работу
Думают, что в Zustand нет DevTools или middleware — на самом деле они есть
Не могут привести конкретный пример кода ни для одной из библиотек


