Настройка State Management (Redux Toolkit) для React-приложения

При разработке крупного React-приложения управление состоянием быстро превращается в хаос: десятки reducer'ов, раздутый store, баги из-за случайной мутации объектов. В типичном проекте бывает до 3000+ строк чистого Redux. Redux Toolkit (RTK) — официальная библиотека, которая решает эти проблемы. Она

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Настройка State Management (Redux Toolkit) для React-приложения
Средний
от 1 дня до 3 дней

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1419
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    983
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1244
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    983
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    998

При разработке крупного React-приложения управление состоянием быстро превращается в хаос: десятки reducer'ов, раздутый store, баги из-за случайной мутации объектов. В типичном проекте бывает до 3000+ строк чистого Redux. Redux Toolkit (RTK) — официальная библиотека, которая решает эти проблемы. Она устраняет избыточный boilerplate: встроенный createSlice генерирует actions и reducers автоматически, Immer под капотом делает мутации безопасными — вы меняете state напрямую, а он остаётся иммутабельным. RTK Query берёт на себя всю работу с API, включая кэширование, оптимистичные обновления и retry с exponential backoff. По нашим данным, внедрение RTK сокращает объём кода управления состоянием на 50% и уменьшает количество багов, связанных с состоянием, на 40%. Экономия времени разработки составляет до 40%, а совокупная стоимость владения (TCO) снижается на 30%.

Мы имеем опыт более 5 лет внедрения RTK в десятках проектов — от простых админок до крупных SaaS-платформ с тысячами пользователей. Наши инженеры гарантируют, что после настройки вы забудете о багах при обновлении стейта, а код будет легко поддерживать. Свяжитесь с нами для консультации, если хотите избавиться от boilerplate и ускорить разработку.

Как Redux Toolkit решает проблему boilerplate?

RTK не просто упрощает Redux, он меняет подход к структуре проекта. Вместо разрозненных файлов вы используете createSlice. Он генерирует actions, reducers и константы автоматически. Immer под капотом делает мутации безопасными: вы меняете state напрямую, а он остаётся иммутабельным. createAsyncThunk и RTK Query решают типовые задачи за вас.

Аспект Redux (без RTK) Redux Toolkit
Создание actions { type: 'cart/ADD_ITEM', payload } вручную cartSlice.actions.addItem(payload)
Иммутабельность Вручную (spread operator) Через Immer — мутации в reducers допустимы
Thunk redux-thunk отдельно createAsyncThunk встроен
Селекторы reselect отдельно createSelector встроен
Серверные данные Самописный код RTK Query встроен
Конфигурация store 20+ строк boilerplate configureStore из одного вызова

Официальная документация Redux Toolkit подтверждает, что RTK — рекомендованный подход.

Как RTK Query упрощает работу с API?

RTK Query генерирует хуки для запросов, кэширует данные и поддерживает оптимистичные обновления. Сравните: при ручном подходе нужно писать useEffect, обрабатывать состояния загрузки и ошибки, синхронизировать с Redux. RTK Query делает это за вас, а система тегов позволяет точечно инвалидировать кэш. В результате объём кода для работы с API сокращается в 2 раза — RTK Query эффективнее ручного fetch.

Подход Объём кода Кэширование Оптимистичные обновления
fetch + useEffect Высокий Вручную Сложно
RTK Query Низкий Автоматически Встроено

Пошаговая настройка RTK Query

  1. Определите базовый URL и заголовки в fetchBaseQuery.
  2. Создайте API с помощью createApi, укажите reducerPath, tagTypes и endpoints.
  3. Генерируйте хуки — useListProductsQuery и useCreateProductMutation готовы к использованию.
  4. Добавьте middleware в store и setupListeners для авто-refetch.
// features/products/productsApi.ts import { createApi, fetchBaseQuery, retry } from '@reduxjs/toolkit/query/react'; import type { RootState } from '@/store'; const baseQuery = fetchBaseQuery({ baseUrl: import.meta.env.VITE_API_URL, prepareHeaders: (headers, { getState }) => { const token = (getState() as RootState).auth.token; if (token) headers.set('Authorization', `Bearer ${token}`); return headers; }, }); // Автоматический retry с exponential backoff const baseQueryWithRetry = retry(baseQuery, { maxRetries: 2 }); export const productsApi = createApi({ reducerPath: 'productsApi', baseQuery: baseQueryWithRetry, tagTypes: ['Product', 'Category'], endpoints: (builder) => ({ listProducts: builder.query<PaginatedResponse<Product>, ProductQuery>({ query: (params) => ({ url: '/products', params }), providesTags: (result) => result ? [...result.items.map(({ id }) => ({ type: 'Product' as const, id })), 'Product'] : ['Product'], // Трансформация ответа для нормализации transformResponse: (raw: ApiResponse<Product[]>) => ({ items: raw.data, total: raw.meta.total, page: raw.meta.page, }), }), createProduct: builder.mutation<Product, CreateProductDto>({ query: (body) => ({ url: '/products', method: 'POST', body }), invalidatesTags: ['Product'], // Оптимистичное обновление async onQueryStarted(body, { dispatch, queryFulfilled }) { const patchResult = dispatch( productsApi.util.updateQueryData('listProducts', {}, (draft) => { draft.items.unshift({ id: 'temp', ...body, createdAt: new Date().toISOString() }); draft.total += 1; }) ); try { await queryFulfilled; } catch { patchResult.undo(); } }, }), }), }); export const { useListProductsQuery, useCreateProductMutation } = productsApi; 

Современная архитектура с RTK

  • src/store/index.ts — конфигурация store
  • src/store/hooks.ts — типизированные useDispatch/useSelector
  • src/store/middleware/errorMiddleware.ts — обработка ошибок
  • src/store/middleware/analyticsMiddleware.ts — трекинг событий
  • src/store/features/auth/ — authSlice, authSelectors, authThunks
  • src/store/features/products/ — productsSlice, productsApi (RTK Query)
  • src/store/features/ui/ — uiSlice

Feature-based структура: каждая доменная область — отдельная директория с slice, селекторами и API.

Что даёт типизация слайсов и selectors?

Типизация слайсов и селекторов — не просто формальность. Она предотвращает целый класс ошибок: передачу неправильного экшна, обращение к несуществующему полю стейта, несоответствие типов в редьюсерах. С помощью TypeScript и встроенных утилит Redux Toolkit (PayloadAction, createAsyncThunk) вы получаете полную типовую безопасность на всех этапах. Это сокращает время отладки на 30% и упрощает рефакторинг.

createSlice с Immer

// features/auth/authSlice.ts import { createSlice, createAsyncThunk, type PayloadAction } from '@reduxjs/toolkit'; interface User { id: string; email: string; role: 'admin' | 'manager' | 'viewer'; permissions: string[]; } interface AuthState { user: User | null; token: string | null; status: 'idle' | 'loading' | 'authenticated' | 'error'; error: string | null; } // createAsyncThunk генерирует pending/fulfilled/rejected actions автоматически export const login = createAsyncThunk( 'auth/login', async (credentials: { email: string; password: string }, { rejectWithValue }) => { try { const res = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(credentials), }); if (!res.ok) { const err = await res.json(); return rejectWithValue(err.message); } return res.json() as Promise<{ user: User; token: string }>; } catch { return rejectWithValue('Ошибка сети'); } } ); export const authSlice = createSlice({ name: 'auth', initialState: { user: null, token: localStorage.getItem('token'), status: 'idle', error: null, } satisfies AuthState, reducers: { // Immer позволяет мутировать state напрямую — под капотом всё иммутабельно logout(state) { state.user = null; state.token = null; state.status = 'idle'; localStorage.removeItem('token'); }, updateProfile(state, action: PayloadAction<Partial<User>>) { if (state.user) { Object.assign(state.user, action.payload); } }, }, extraReducers: (builder) => { builder .addCase(login.pending, (state) => { state.status = 'loading'; state.error = null; }) .addCase(login.fulfilled, (state, action) => { state.user = action.payload.user; state.token = action.payload.token; state.status = 'authenticated'; localStorage.setItem('token', action.payload.token); }) .addCase(login.rejected, (state, action) => { state.status = 'error'; state.error = action.payload as string; }); }, }); export const { logout, updateProfile } = authSlice.actions; 

Middleware и конфигурация store

// store/middleware/errorMiddleware.ts & analyticsMiddleware.ts import type { Middleware } from '@reduxjs/toolkit'; import { isRejectedWithValue } from '@reduxjs/toolkit'; import { toast } from 'sonner'; export const errorMiddleware: Middleware = () => (next) => (action) => { if (isRejectedWithValue(action)) { const message = (action.payload as any)?.message ?? 'Неизвестная ошибка'; toast.error(message); } return next(action); }; const TRACKED_ACTIONS = ['cart/addItem', 'auth/login/fulfilled', 'order/submit/fulfilled']; export const analyticsMiddleware: Middleware = () => (next) => (action) => { if (typeof action.type === 'string' && TRACKED_ACTIONS.includes(action.type)) { window.gtag?.('event', action.type.replace(/\//g, '_'), { payload: JSON.stringify(action.payload), }); } return next(action); }; 
// store/index.ts import { configureStore } from '@reduxjs/toolkit'; import { setupListeners } from '@reduxjs/toolkit/query'; export const store = configureStore({ reducer: { auth: authSlice.reducer, cart: cartSlice.reducer, ui: uiSlice.reducer, [productsApi.reducerPath]: productsApi.reducer, }, middleware: (getDefault) => getDefault() .concat(productsApi.middleware) .concat(errorMiddleware) .concat(analyticsMiddleware), }); // Автоматический refetch при reconnect и focus setupListeners(store.dispatch); 

Получите консультацию по вашему проекту — мы поможем внедрить RTK эффективно.

Тестирование reducers

Reducers — чистые функции, тестируются без React, без рендера, без моков. Это ускоряет написание тестов и повышает их надёжность.

// features/cart/cartSlice.test.ts import { cartSlice, addItem, removeItem, selectCartTotal } from './cartSlice'; const { reducer } = cartSlice; describe('cartSlice', () => { it('добавляет новый товар', () => { const state = reducer(undefined, addItem({ id: '1', name: 'Test', price: 100, image: '' })); expect(state.items).toHaveLength(1); expect(state.items[0].qty).toBe(1); }); it('увеличивает qty для существующего товара', () => { const item = { id: '1', name: 'Test', price: 100, image: '' }; const state = [addItem(item), addItem(item)].reduce(reducer, undefined); expect(state.items[0].qty).toBe(2); }); it('корректно считает итог с купоном', () => { const rootState = { cart: { items: [{ id: '1', price: 200, qty: 2 }], couponDiscount: 10 } }; expect(selectCartTotal(rootState as any)).toBe(360); // 400 - 10% }); }); 

Объём работ по настройке RTK

Настройка Redux Toolkit включает аудит текущей архитектуры, создание типизированных feature-слайсов, настройку RTK Query с кэшированием и оптимистичными обновлениями, интеграцию middleware для обработки ошибок и аналитики, а также unit-тестирование reducers и selectors. Мы также предоставляем документацию соглашений по именованию и поддерживаем интеграцию через код-ревью.

Типичные ошибки при настройке RTK
  • Неправильная типизация экшнов в extraReducers (используйте builder.addCase).
  • Забывают добавлять middleware RTK Query в configureStore.
  • Слишком частые инвалидации тегов (используйте providesTags с умом).
  • Отсутствие обработки ошибок в createAsyncThunk (используйте rejectWithValue).

Если вам нужна настройка Redux Toolkit под ваш проект — свяжитесь с нами, мы оценим задачу и предложим оптимальное решение.

Сроки реализации

  • Настройка store, feature-slices для основных доменов, типизация — 1 неделя.
  • RTK Query endpoints, интеграция с компонентами, оптимистичные обновления — 1 неделя.
  • Middleware (error handling, analytics), мемоизированные селекторы — 1 неделя.
  • Unit-тесты reducers и selectors, документация соглашений по именованию actions — 1 неделя.