PurpleSchool — курсы программирования онлайн
  • Пути
    • Frontend React разработчик
    • Frontend Vue разработчик
    • Backend разработчик Node.js
    • Fullstack разработчик React / Node.js
    • Mobile разработчик React Native
    • Backend разработчик Golang
    • Devops инженер
    • Backend разработчик Python
  • AI для кодаНовое
  • О нас
    • Отзывы
    • Реферальная программа
    • О компании
    • Контакты
  • Иконка открытия меню
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
PurpleSchool — платформа бесплатных roadmap и курсов для разработчиков
ютуб иконка
Telegram иконка
VK иконка
VK иконка
Курсы
ГлавнаяКаталог курсовFrontendBackendFullstack
Практика
КарьераПроектыPurpleПлюс
Материалы
БлогБаза знаний
Документы
Договор офертаПолитика конфиденциальностиПроверка сертификатаМиграция курсовРеферальная программа
Реквизиты
ИП Ларичев Антон АндреевичИНН 773373765379contact@purpleschool.ru

PurpleSchool © 2020 -2026 Все права защищены

  • Курсы
    • FrontendИконка стрелки
    • AI разработкаИконка стрелки
    • BackendИконка стрелки
    • DevOpsИконка стрелки
    • MobileИконка стрелки
    • ТестированиеИконка стрелки
    • Soft-skillsИконка стрелки
    • ДизайнИконка стрелки
    Иконка слояПерейти в каталог курсов
  • Бесплатно
    • Курсы
    • JavaScript Основы разработкиPython Основы PythonCSS CSS FlexboxКарта развития
    • База знанийИконка стрелки
    • Новостные рассылкиИконка стрелки
  • PurpleSchool — курсы программирования онлайн
    • AI для кодаНовое
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
    Главная
    Сообщество
    Zustand vs Redux Toolkit: что выбрать в 2024 году

    Zustand vs Redux Toolkit: что выбрать в 2024 году

    Аватар автора Zustand vs Redux Toolkit: что выбрать в 2024 году

    Антон Ларичев

    Иконка календаря26 апреля 2026
    ReactState ManagementZustandRedux ToolkitJavaScriptmiddleИконка уровня middle
    Картинка поста Zustand vs Redux Toolkit: что выбрать в 2024 году

    Введение

    Управление состоянием — одна из ключевых задач в любом React-приложении. Долгое время Redux был стандартом де-факто, но сегодня у него появился серьёзный конкурент — Zustand. Оба инструмента решают одну задачу, но делают это принципиально по-разному.

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

    Архитектура и философия

    Redux Toolkit

    Redux Toolkit (RTK) — это официальный набор инструментов для работы с Redux. Он построен на принципах Flux-архитектуры: единственный глобальный store, иммутабельные обновления через reducers и строгий однонаправленный поток данных.

    RTK значительно упрощает работу с Redux: убирает шаблонный код, встраивает Immer для мутабельных обновлений и предоставляет RTK Query для работы с API.

    Zustand

    Zustand — минималистичная библиотека на основе хуков. Она не навязывает строгую архитектуру: вы создаёте store как обычный JavaScript-объект с состоянием и действиями. Под капотом используется паттерн наблюдателя и контекст React не нужен вовсе.

    Сравнение на практике

    Установка и начало работы

    # Redux Toolkit
    npm install @reduxjs/toolkit react-redux
    
    # Zustand
    npm install zustand
    

    Создание хранилища: Redux Toolkit

    import { createSlice, configureStore, PayloadAction } from '@reduxjs/toolkit';
    
    // Определяем тип состояния
    interface CounterState {
      value: number;
    }
    
    const initialState: CounterState = { value: 0 };
    
    // Создаём slice с редьюсерами и экшенами
    const counterSlice = createSlice({
      name: 'counter',
      initialState,
      reducers: {
        increment: (state) => {
          state.value += 1; // Immer позволяет писать мутации
        },
        decrement: (state) => {
          state.value -= 1;
        },
        setValue: (state, action: PayloadAction<number>) => {
          state.value = action.payload;
        },
      },
    });
    
    export const { increment, decrement, setValue } = counterSlice.actions;
    
    // Настраиваем глобальный store
    export const store = configureStore({
      reducer: {
        counter: counterSlice.reducer,
      },
    });
    
    export type RootState = ReturnType<typeof store.getState>;
    export type AppDispatch = typeof store.dispatch;
    
    // Провайдер оборачивает всё приложение
    import { Provider } from 'react-redux';
    import { store } from './store';
    
    function App() {
      return (
        <Provider store={store}>
          <Counter />
        </Provider>
      );
    }
    
    import { useDispatch, useSelector } from 'react-redux';
    import { increment, decrement } from './counterSlice';
    import { RootState } from './store';
    
    function Counter() {
      const value = useSelector((state: RootState) => state.counter.value);
      const dispatch = useDispatch();
    
      return (
        <div>
          <span>{value}</span>
          <button onClick={() => dispatch(increment())}>+</button>
          <button onClick={() => dispatch(decrement())}>-</button>
        </div>
      );
    }
    

    Создание хранилища: Zustand

    import { create } from 'zustand';
    
    // Типизируем store
    interface CounterStore {
      value: number;
      increment: () => void;
      decrement: () => void;
      setValue: (val: number) => void;
    }
    
    // Создаём store — всё в одном месте
    const useCounterStore = create<CounterStore>((set) => ({
      value: 0,
      increment: () => set((state) => ({ value: state.value + 1 })),
      decrement: () => set((state) => ({ value: state.value - 1 })),
      setValue: (val) => set({ value: val }),
    }));
    
    // Никаких провайдеров не нужно — используем хук напрямую
    function Counter() {
      const { value, increment, decrement } = useCounterStore();
    
      return (
        <div>
          <span>{value}</span>
          <button onClick={increment}>+</button>
          <button onClick={decrement}>-</button>
        </div>
      );
    }
    

    Асинхронные операции

    RTK предоставляет createAsyncThunk для работы с API:

    import { createAsyncThunk } from '@reduxjs/toolkit';
    
    // Асинхронный thunk для загрузки данных пользователя
    export const fetchUser = createAsyncThunk(
      'user/fetchById',
      async (userId: string) => {
        const response = await fetch(`/api/users/${userId}`);
        return response.json();
      }
    );
    

    В Zustand асинхронные действия — просто async-функции:

    interface UserStore {
      user: User | null;
      loading: boolean;
      fetchUser: (id: string) => Promise<void>;
    }
    
    const useUserStore = create<UserStore>((set) => ({
      user: null,
      loading: false,
      // Асинхронное действие — обычная async-функция
      fetchUser: async (id) => {
        set({ loading: true });
        const response = await fetch(`/api/users/${id}`);
        const user = await response.json();
        set({ user, loading: false });
      },
    }));
    

    Производительность и селекторы

    RTK использует reselect для мемоизированных селекторов:

    import { createSelector } from '@reduxjs/toolkit';
    
    // Мемоизированный селектор — пересчитывается только при изменении зависимостей
    const selectFilteredItems = createSelector(
      (state: RootState) => state.items.list,
      (state: RootState) => state.items.filter,
      (list, filter) => list.filter(item => item.category === filter)
    );
    

    Zustand решает проблему лишних перерендеров через подписку на конкретные части состояния:

    // Компонент перерендерится только при изменении поля value
    const value = useCounterStore((state) => state.value);
    

    Когда выбирать Redux Toolkit

    • Крупное enterprise-приложение с большой командой
    • Нужны мощные DevTools для отладки и time-travel debugging
    • Сложная бизнес-логика с множеством взаимозависимых состояний
    • Уже используете RTK Query для управления серверным состоянием
    • Требуется строгая архитектура и единые соглашения в команде

    Когда выбирать Zustand

    • Небольшие или средние проекты
    • Нужно быстро стартовать без boilerplate-кода
    • Небольшая команда или соло-разработка
    • Хотите минимальный бандл (Zustand весит около 1 КБ)
    • Нужно локальное состояние нескольких компонентов без глобального store

    Частые ошибки

    Ошибка 1: Подписка на весь store в Zustand

    // Плохо: компонент перерендерится при любом изменении store
    const store = useCounterStore();
    
    // Хорошо: подписываемся только на нужное поле
    const value = useCounterStore((state) => state.value);
    

    Ошибка 2: Мутация состояния в Redux напрямую

    // Плохо: прямая мутация вне Immer-контекста вызовет ошибки
    const badReducer = (state, action) => {
      state.items.push(action.payload); // ошибка вне createSlice
      return state;
    };
    
    // Хорошо: используем createSlice — Immer встроен
    const goodSlice = createSlice({
      name: 'items',
      initialState: { items: [] },
      reducers: {
        addItem: (state, action) => {
          state.items.push(action.payload); // безопасно внутри createSlice
        },
      },
    });
    

    Ошибка 3: Хранить серверные данные в Zustand вместо React Query

    Zustand предназначен для клиентского состояния (UI, фильтры, модалки). Для серверных данных лучше использовать React Query или RTK Query — они берут на себя кэширование, дедупликацию запросов и инвалидацию.

    Ошибка 4: Один огромный store в Zustand

    // Плохо: всё в одном store — сложно поддерживать
    const useAppStore = create(() => ({
      user: null,
      cart: [],
      theme: 'dark',
      notifications: [],
    }));
    
    // Хорошо: разделяем по доменам
    const useUserStore = create(...);
    const useCartStore = create(...);
    const useThemeStore = create(...);
    

    Заключение

    Zustand и Redux Toolkit — отличные инструменты, каждый со своей нишей. Zustand побеждает в простоте, скорости разработки и размере бандла. Redux Toolkit — в структурированности, экосистеме и инструментах отладки.

    Для большинства новых проектов мы рекомендуем начинать с Zustand и переходить на RTK только тогда, когда сложность приложения этого действительно требует. Помните: лучший инструмент — тот, который решает вашу конкретную задачу с минимальными накладными расходами.

    Иконка глаза415

    Постройте личный план изучения Neovim - практика и настройка до уровня Middle — бесплатно!

    Neovim - практика и настройка — часть карты развития Frontend, Backend, DevOps

    • step100+ шагов развития
    • lessons30 бесплатных лекций
    • lessons300 бонусных рублей на счет

    Бесплатные лекции

    Лучшие курсы по теме

    изображение курса

    Основы Git

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.9
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    Основы JavaScript

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    Продвинутый JavaScript

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее

    Похожие статьи

    Картинка поста React Hooks: полный гайд по useState, useEffect, useCallback, useMemo
    Иконка аватараАнтон
    Иконка календаря31 мая 2026
    ReactJavaScriptHooks+ 1middleИконка уровня middle

    React Hooks: полный гайд по useState, useEffect, useCallback, useMemo

    Полный гайд по React Hooks: useState, useEffect, useCallback и useMemo. Разбираем синтаксис, примеры использования и типичные ошибки.

    Иконка чипа+1
    Иконка глаза185
    Иконка комментариев0
    Картинка поста Асинхронность в JavaScript: Event Loop, промисы и async/await
    Иконка аватараАнтон
    Иконка календаря04 июня 2026
    JavaScriptАсинхронностьEvent Loop+ 2middleИконка уровня middle

    Асинхронность в JavaScript: Event Loop, промисы и async/await

    Разбираем асинхронность в JavaScript: как работает Event Loop, чем промисы лучше колбэков и зачем нужен async/await на практике.

    Иконка чипа0
    Иконка глаза127
    Иконка комментариев0
    Картинка поста WebSocket на Node.js: строим real-time чат с нуля за час
    Иконка аватараАнтон
    Иконка календаря26 мая 2026
    Node.jsWebSocketJavaScript+ 2middleИконка уровня middle

    WebSocket на Node.js: строим real-time чат с нуля за час

    WebSocket на Node.js: пошаговое руководство по созданию real-time чата с библиотекой ws, обработкой подключений и broadcast-сообщений.

    Иконка чипа0
    Иконка глаза271
    Иконка комментариев0
    Иконка чипа0

    Комментарии

    1
    Иконка аватара
    Andrei Miagkov

    05.05.2026

    Дата похоже старая в заголовке)