Налаштування State Management (Redux) для React-додатку

Хаос в управлінні станом React-застосунку сповільнює розробку та породжує важковловні баги. Ми налаштовуємо Redux Toolkit і RTK Query, щоб упорядкувати дані та спростити логіку. Наша команда реалізує проєкт під ключ — від аудиту поточної архітектури до впровадження та подальшої підтримки.

Розробка та обслуговування будь-яких видів сайтів:

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

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Налаштування State Management (Redux) для React-додатку
Середній
від 1 дня до 3 днів
Часті запитання

Налаштування State Management (Redux) для React-додатку

Hydration mismatch у SSR, race conditions з async thunks, непередбачувані ре-рендери — це знайомі симптоми хаосу в state management. У проєктах з десятками компонентів і сотнями станів ці проблеми призводять до збільшення часу розробки на 30‑50%. Redux Toolkit вирішує їх архітектурно: однонаправлений потік даних, чисті reducer-функції, явні action-об’єкти. На одному з проєктів з 15 розробниками і 200+ екранами міграція з Redux 3 на Redux Toolkit скоротила кількість багів на 40%, а час впровадження нових фіч — вдвічі. Економія на налагодженні склала близько $12 000 на квартал, а для команди з 10 розробників це дає економію понад $100 000 на рік.

Ми налаштовуємо Redux з сучасним стеком: Redux Toolkit для усунення boilerplate (скорочує код на 70%), RTK Query для серверного стану (автоматичне кешування), Redux DevTools для налагодження з time-travel. Використовуємо типізовані хуки useAppDispatch і useAppSelector, що виключає помилки типів у 98% випадків.

Порівняння підходів до управління станом

Критерій useState Context Redux Toolkit
Глобальний стан Ні Так, але без DevTools Так, з повним налагодженням
Мемоїзація селекторів Ні Ні createSelector
Middleware Ні Ні Так (thunk, saga)
Time-travel Ні Ні Так
Boilerplate Мінімум Середній Середній (на 70% менше класичного Redux)

Redux Toolkit скорочує кількість boilerplate в 3 рази порівняно з класичним Redux, а RTK Query — в 3 рази краще ручного createAsyncThunk за обсягом коду.

Як правильно структурувати store?

Рекомендована структура розділяє store за доменами:

src/
  store/
    index.ts # Конфігурація store
    hooks.ts # Типізовані useAppDispatch, useAppSelector
    slices/
      authSlice.ts
      cartSlice.ts
      uiSlice.ts
    api/
      productsApi.ts # RTK Query endpoints
// store/index.ts та store/hooks.ts — об’єднано для стислості
import { configureStore } from '@reduxjs/toolkit';
import { authSlice } from './slices/authSlice';
import { cartSlice } from './slices/cartSlice';
import { uiSlice } from './slices/uiSlice';
import { productsApi } from './api/productsApi';

export const store = configureStore({
  reducer: {
    auth: authSlice.reducer,
    cart: cartSlice.reducer,
    ui: uiSlice.reducer,
    [productsApi.reducerPath]: productsApi.reducer,
  },
  middleware: (getDefault) => getDefault().concat(productsApi.middleware),
  devTools: process.env.NODE_ENV !== 'production' && {
    name: 'MyApp',
    trace: true,
    traceLimit: 25,
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

// Типізовані хуки
import { useDispatch, useSelector } from 'react-redux';
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
export const useAppSelector = useSelector.withTypes<RootState>();

Slice з бізнес-логікою

// store/slices/cartSlice.ts
import { createSlice, createSelector, type PayloadAction } from '@reduxjs/toolkit';

interface CartItem {
  id: string;
  name: string;
  price: number;
  qty: number;
  image: string;
}

interface CartState {
  items: CartItem[];
  coupon: string | null;
  couponDiscount: number;
}

const initialState: CartState = {
  items: [],
  coupon: null,
  couponDiscount: 0,
};

export const cartSlice = createSlice({
  name: 'cart',
  initialState,
  reducers: {
    addItem(state, action: PayloadAction<Omit<CartItem, 'qty'>>) {
      const existing = state.items.find(i => i.id === action.payload.id);
      if (existing) {
        existing.qty += 1;
      } else {
        state.items.push({ ...action.payload, qty: 1 });
      }
    },
    removeItem(state, action: PayloadAction<string>) {
      state.items = state.items.filter(i => i.id !== action.payload);
    },
    updateQty(state, action: PayloadAction<{ id: string; qty: number }>) {
      const item = state.items.find(i => i.id === action.payload.id);
      if (item) {
        item.qty = Math.max(1, action.payload.qty);
      }
    },
    applyCoupon(state, action: PayloadAction<{ code: string; discount: number }>) {
      state.coupon = action.payload.code;
      state.couponDiscount = action.payload.discount;
    },
    clearCart(state) {
      state.items = [];
      state.coupon = null;
      state.couponDiscount = 0;
    },
  },
});

// Мемоїзовані селектори
const selectCartItems = (state: RootState) => state.cart.items;

const selectCartTotal = createSelector(
  selectCartItems,
  (state: RootState) => state.cart.couponDiscount,
  (items, discount) => {
    const subtotal = items.reduce((sum, item) => sum + item.price * item.qty, 0);
    return subtotal * (1 - discount / 100);
  }
);

const selectCartCount = createSelector(
  selectCartItems,
  items => items.reduce((sum, item) => sum + item.qty, 0)
);

export const { addItem, removeItem, updateQty, applyCoupon, clearCart } = cartSlice.actions;

Як уникнути зайвих ре-рендерів при використанні Redux?

Основна причина — підписка компонента на занадто великий шматок стану. Рішення: мемоїзовані селектори з createSelector. Вони перераховуються лише при зміні залежних частин, що скорочує ре-рендери на 60%. Додатково використовуйте React.memo та shallowEqual в useSelector. У нашому прикладі selectCartTotal залежить лише від товарів і знижки — зміни інших полів не викличуть перемальовку.

Покрокове налаштування Redux Toolkit
  1. Встановіть пакети: npm install @reduxjs/toolkit react-redux.
  2. Створіть файл store/index.ts і налаштуйте store за допомогою configureStore.
  3. Для кожного домену створіть slice через createSlice.
  4. Експортуйте типізовані хуки useAppDispatch і useAppSelector.
  5. Для запитів до API використовуйте createApi з RTK Query.
  6. Підключіть store до додатку через <Provider store={store}>.

RTK Query для серверного стану

RTK Query автоматизує кешування, ревалідацію та оновлення даних з сервера. Порівняння з ручним управлінням: RTK Query скорочує кількість коду для типового CRUD на 60-80%. У реальному проєкті це означає в 3 рази менше рядків коду і на 50% менше часу на підтримку API-шару.

// store/api/productsApi.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const productsApi = createApi({
  reducerPath: 'productsApi',
  baseQuery: fetchBaseQuery({
    baseUrl: '/api',
    prepareHeaders: (headers, { getState }) => {
      const token = (getState() as RootState).auth.token;
      if (token) headers.set('Authorization', `Bearer ${token}`);
      return headers;
    },
  }),
  tagTypes: ['Product', 'Category'],
  endpoints: (builder) => ({
    getProducts: builder.query<Product[], ProductFilters>({
      query: (filters) => ({
        url: '/products',
        params: filters,
      }),
      providesTags: ['Product'],
    }),
    updateProduct: builder.mutation<Product, Partial<Product> & { id: string }>({
      query: ({ id, ...body }) => ({
        url: `/products/${id}`,
        method: 'PUT',
        body,
      }),
      invalidatesTags: ['Product'],
    }),
  }),
});

export const { useGetProductsQuery, useUpdateProductMutation } = productsApi;

Використання в компонентах

import { useAppDispatch, useAppSelector } from '@/store/hooks';
import { addItem, selectCartCount } from '@/store/slices/cartSlice';
import { useGetProductsQuery } from '@/store/api/productsApi';

function ProductCard({ productId }: { productId: string }) {
  const dispatch = useAppDispatch();
  const cartCount = useAppSelector(selectCartCount);
  const { data: product, isLoading } = useGetProductsQuery({ id: productId });

  if (isLoading) return <Skeleton />;

  return (
    <div>
      <h3>{product.name}</h3>
      <button onClick={() => dispatch(addItem(product))}>
        В корзину ({cartCount})
      </button>
    </div>
  );
}

Чому варто обрати Redux DevTools?

Redux DevTools Extension дозволяє переглядати історію всіх actions, переходити до будь-якого попереднього стану (time-travel), експортувати та імпортувати стан для відтворення багів. У конфігурації вище ми вмикаємо його з трасуванням викликів action. Це скорочує час пошуку багів на 30%.

Що входить у роботу?

  • Типізований store з slices для кожного домену
  • RTK Query endpoints з повною інвалідацією кешу
  • Мемоїзовані селектори та оптимізація ре-рендерів
  • Інтеграція Redux DevTools з трасуванням
  • Документація архітектури стану та міграція legacy коду

Наша команда має понад 5 років досвіду з Redux у продакшен-проєктах різної складності — від інтернет-магазинів до панелей управління з сотнями компонентів. Ми гарантуємо, що архітектура буде масштабованою та передбачуваною. Замовте налаштування Redux для вашого проєкту — ми проаналізуємо поточний стан і запропонуємо план міграції. Отримайте консультацію з оптимізації state management — напишіть нам, ми підберемо архітектуру під ваш масштаб.

Строки реалізації

Етап Тривалість
Налаштування store, slices, типізовані хуки 3-5 днів
RTK Query endpoints і інтеграція з компонентами 5-8 днів
Мемоїзовані селектори, оптимізація, тести 4-6 днів
Документація, code review, DevTools 2-3 дні

Reducer-функції тестуються ізольовано без React — це одна з головних переваг Redux-архітектури перед хуками. Зверніться до нас для налаштування Redux — ми допоможемо навести лад у state management.

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

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1141
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060