Зачем нужно управление состоянием
▸Проблема prop drilling
Когда данные нужно передать через много уровней компонентов, возникает prop drilling. Это делает код шумным и трудноподдерживаемым.
▸Когда нужно управление состоянием
Redux Toolkit: современный подход
▸Установка и настройка
1npm install @reduxjs/toolkit react-redux
▸Создание slice
1import { createSlice, configureStore } from '@reduxjs/toolkit';23const userSlice = createSlice({4 name: 'user',5 initialState: { name: '', email: '', isAuthenticated: false },6 reducers: {7 setUser: (state, action) => {8 state.name = action.payload.name;9 state.email = action.payload.email;10 state.isAuthenticated = true;11 },12 logout: (state) => {13 state.name = '';14 state.email = '';15 state.isAuthenticated = false;16 },17 },18});1920export const { setUser, logout } = userSlice.actions;21export default userSlice.reducer;
▸Store и Provider
1const store = configureStore({2 reducer: { user: userSlice.reducer },3});45function App() {6 return (7 <Provider store={store}>8 <MyApp />9 </Provider>10 );11}
▸Использование в компонентах
1import { useSelector, useDispatch } from 'react-redux';23function UserProfile() {4 const { name, email } = useSelector(state => state.user);5 const dispatch = useDispatch();67 return (8 <div>9 <p>{name} - {email}</p>10 <button onClick={() => dispatch(logout())}>Выйти</button>11 </div>12 );13}
Zustand: минимальное управление состоянием
▸Установка
1npm install zustand
▸Создание store
1import { create } from 'zustand';23const useUserStore = create((set) => ({4 name: '',5 email: '',6 isAuthenticated: false,7 setUser: (name, email) => set({ name, email, isAuthenticated: true }),8 logout: () => set({ name: '', email: '', isAuthenticated: false }),9}));
▸Использование
1function UserProfile() {2 const name = useUserStore(state => state.name);3 const logout = useUserStore(state => state.logout);45 return (6 <div>7 <p>{name}</p>8 <button onClick={logout}>Выйти</button>9 </div>10 );11}
Сравнение API
▸Объявление состояния
Redux требует создать slice с action creators и reducer. Zustand — просто объект с методами. Zustand значительно компактнее.
▸Доступ к состоянию
Redux использует useSelector и useDispatch. Zustand — кастомные хуки для каждого store. Оба подхода работают с selectors для оптимизации ре-рендеров.
▸Мутации
Redux Toolkit позволяет мутировать state напрямую (immutability через Immer). Zustand также поддерживает мутации — вызов set() обновляет state.
Производительность
▸Ре-рендеры
Оба библиотеки оптимизированы. Redux использует shallow comparison selectors. Zustand по умолчанию сравнивает ссылки, но поддерживает кастомные selectors.
▸Bundle size
Zustand: ~1.1 KB (minified). Redux Toolkit + React Redux: ~11 KB. Разница в 10 раз.
▸Provider overhead
Redux требует Provider обертку. Zustand — нет, store доступен отовсюду. Это упрощает тестирование и tree-shaking.
TypeScript
▸Redux Toolkit
1interface UserState {2 name: string;3 email: string;4 isAuthenticated: boolean;5}67const userSlice = createSlice({8 name: 'user',9 initialState: {} as UserState,10 reducers: {11 setUser: (state, action: PayloadAction<{name: string; email: string}>) => {12 state.name = action.payload.name;13 state.email = action.payload.email;14 },15 },16});
▸Zustand
1interface UserState {2 name: string;3 email: string;4 setUser: (name: string, email: string) => void;5}67const useUserStore = create<UserState>((set) => ({8 name: '',9 email: '',10 setUser: (name, email) => set({ name, email }),11}));
Middleware и расширения
▸Redux middleware
Redux поддерживает middleware для side effects (thunk, saga), логирования, devtools. Redux DevTools — мощный инструмент для отладки.
▸Zustand middleware
1import { persist } from 'zustand/middleware';23const useStore = create(4 persist(5 (set) => ({ count: 0, increment: () => set(state => ({ count: state.count + 1 })) }),6 { name: 'counter-storage' }7 )8);
Когда выбирать Redux
Когда выбирать Zustand
Заключение
Redux Toolkit и Zustand — оба отличные выборы. Redux — зрелая экосистема с богатыми возможностями. Zustand — минималистичная альтернатива с маленьким bundle size. Выбирайте исходя из масштаба проекта и требований команды.