Redux Toolkit

Налаштування Store з configureStore

Redux Toolkit змінює підхід до створення store, автоматизуючи найкращі практики та усуваючи багато boilerplate коду. Давайте розберемося, як це працює і чому це важливо.

Налаштування Store з configureStore

Redux Toolkit змінює підхід до створення store, автоматизуючи найкращі практики та усуваючи багато boilerplate коду. Давайте розберемося, як це працює і чому це важливо.

Проблема класичного підходу

До появи Redux Toolkit налаштування store вимагало значних зусиль:

// Класичний Redux - багато ручної роботи
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import rootReducer from './reducers'

const composeEnhancers = (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose

const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk)))

Що не так з цим кодом?

❌ Багато boilerplate

  • Імпорт декількох функцій
  • Ручне налаштування DevTools
  • Ручне підключення middleware

❌ Відсутність захисту

  • Немає перевірок на мутацію state
  • Немає перевірок на non-serializable дані
  • Легко допустити помилку

❌ Складність для новачків

  • Незрозуміло, що таке compose та enhancers
  • Потрібно знати про DevTools extension API
  • Бар'єр входу занадто високий

Рішення: configureStore

Redux Toolkit надає функцію configureStore, яка інкапсулює всі найкращі практики:

import { configureStore } from '@reduxjs/toolkit'

const store = configureStore({
    reducer: {
        // Ваші reducers
    },
})

Одна функція замінює весь boilerplate і додає captures потужні можливості автоматично.


Встановлення

Крок 1: Встановлення пакетів

Redux Toolkit вже включає в себе Redux core, тому окремо redux встановлювати не потрібно.

npm install @reduxjs/toolkit react-redux
Примітка: Пакет react-redux потрібен для зв'язки Redux store з React компонентами через hooks та Provider.

Крок 2: Створення store файлу

Створіть файл src/store.js (або src/app/store.js для більших проєктів).


Повний API configureStore

Функція configureStore приймає об'єкт конфігурації з наступними параметрами:

ПараметрТипОбов'язковийОпис
reducerObject/FunctionRoot reducer або об'єкт з slice reducers
middlewareFunctionФункція для налаштування middleware
devToolsBoolean/ObjectУвімкнути DevTools (за замовчуванням true у dev)
preloadedStateObjectПочатковий state (для SSR, hydration)
enhancersArray/FunctionДодаткові store enhancers
Порада: У 90% випадків вам потрібен лише параметр reducer. Решта параметрів для advanced use cases.

Базова конфігурація

Простий приклад

src/store.js
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './features/counter/counterSlice'
import userReducer from './features/user/userSlice'

export const store = configureStore({
    reducer: {
        counter: counterReducer,
        user: userReducer,
    },
})

Структура state

З такою конфігурацією ваш глобальний state матиме структуру:

{
  counter: { /* state з counterSlice */ },
  user: { /* state з userSlice */ }
}
Ключі в об'єкті reducer стають ключами в глобальному state. Вибирайте їх обдумано!

Middleware Pipeline

Що таке Middleware?

Middleware (проміжне програмне забезпечення) — це функції, які перехоплюють кожен dispatched action перед тим, як він досягне reducer.

Loading diagram...
graph LR
    A[Component] -->|dispatch action| B[Middleware 1]
    B --> C[Middleware 2]
    C --> D[Middleware 3]
    D --> E[Reducers]
    E --> F[New State]
    F -->|re-render| A

    style A fill:#3b82f6,stroke:#1d4ed8,color:#ffffff
    style B fill:#f59e0b,stroke:#b45309,color:#ffffff
    style C fill:#f59e0b,stroke:#b45309,color:#ffffff
    style D fill:#f59e0b,stroke:#b45309,color:#ffffff
    style E fill:#10b981,stroke:#059669,color:#ffffff
    style F fill:#64748b,stroke:#334155,color:#ffffff

Middleware за замовчуванням

configureStore автоматично додає наступні middleware:

  1. Redux Thunk — для асинхронних actions
  2. Immutability Check (dev only) — виявляє мутації state
  3. Serializability Check (dev only) — виявляє non-serializable значення
Важливо: Immutability та Serializability перевірки автоматично вимикаються в production (NODE_ENV === 'production') для збереження performance.

Додавання кастомного middleware

Використовуйте callback getDefaultMiddleware():

src/store.js
import { configureStore } from '@reduxjs/toolkit'
import logger from 'redux-logger'
import counterReducer from './features/counter/counterSlice'

export const store = configureStore({
    reducer: {
        counter: counterReducer,
    },
    middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
})

Налаштування вбудованих middleware

Можна налаштувати поведінку вбудованих middleware:

middleware: (getDefaultMiddleware) =>
  getDefaultMiddleware({
    // Вимкнути immutability check для specific шляхів
    immutableCheck: {
      ignoredPaths: ['items.data'],
    },
    // Вимкнути serializability check для певних actions
    serializableCheck: {
      ignoredActions: ['your/action/type'],
      ignoredPaths: ['items.date'],
    },
    // Повністю вимкнути thunk (якщо не використовуєте)
    thunk: false,
  }),
Use case: Вимикайте перевірки для великих списків (10000+ items), щоб уникнути lag при розробці.

Кастомний logger middleware

Приклад створення простого logger middleware:

const loggerMiddleware = (store) => (next) => (action) => {
    console.group(action.type)
    console.log('Dispatching:', action)
    console.log('Previous State:', store.getState())

    const result = next(action)

    console.log('Next State:', store.getState())
    console.groupEnd()

    return result
}

export const store = configureStore({
    reducer: {
        counter: counterReducer,
    },
    middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(loggerMiddleware),
})

DevTools інтеграція

Автоматичне підключення

Redux DevTools працюють "з коробки" без налаштувань:

// DevTools автоматично підключені!
export const store = configureStore({
    reducer: {
        counter: counterReducer,
    },
})

Налаштування DevTools

Для advanced функцій передайте об'єкт конфігурації:

export const store = configureStore({
    reducer: {
        counter: counterReducer,
    },
    devTools: {
        // Кількість actions в історії
        maxAge: 50,

        // Trace для actions (показує stack trace)
        trace: true,

        // Приховати певні екшени
        actionsBlacklist: ['SOME_NOISY_ACTION'],

        // Sanitize state (приховати чутливі дані)
        stateSanitizer: (state) => ({
            ...state,
            user: state.user ? { ...state.user, password: '***HIDDEN***' } : null,
        }),
    },
})

Вимкнення DevTools у production

// DevTools автоматично вимкнені в production
export const store = configureStore({
    reducer: {
        counter: counterReducer,
    },
    // devTools: true — за замовчуванням у dev, false у prod
})

Організація множинних Reducers

Feature-based структура (рекомендовано)

src/
├── store.js
├── features/
│   ├── auth/
│   │   ├── authSlice.js
│   │   └── AuthForm.jsx
│   ├── todos/
│   │   ├── todosSlice.js
│   │   └── TodoList.jsx
│   └── posts/
│       ├── postsSlice.js
│       └── PostsList.jsx
src/store.js
import { configureStore } from '@reduxjs/toolkit'
import authReducer from './features/auth/authSlice'
import todosReducer from './features/todos/todosSlice'
import postsReducer from './features/posts/postsSlice'

export const store = configureStore({
    reducer: {
        auth: authReducer,
        todos: todosReducer,
        posts: postsReducer,
    },
})
Переваги: Кожна feature є самодостатнім модулем. Легко видалити, перенести або протестувати окремо.

Code Splitting для Reducers

Для великих застосунків можна lazy load slices:

src/store.js
import { configureStore } from '@reduxjs/toolkit'
import authReducer from './features/auth/authSlice'

export const store = configureStore({
    reducer: {
        auth: authReducer,
        // Інші reducers додадуться динамічно
    },
})

// Функція для додавання reducers динамічно
export function injectReducer(key, reducer) {
    store.replaceReducer({
        ...store.getState(),
        [key]: reducer,
    })
}

Використання:

// У компоненті, який lazy loads
import { lazy, Suspense } from 'react'
import { injectReducer } from './store'

const AdminPanel = lazy(async () => {
    const module = await import('./features/admin/adminSlice')
    injectReducer('admin', module.default)
    return import('./features/admin/AdminPanel')
})

Preloaded State (SSR та Hydration)

Для Server-Side Rendering передайте початковий state:

server.js
import { configureStore } from '@reduxjs/toolkit'
import rootReducer from './rootReducer'

// На сервері отримуємо дані
const preloadedState = {
    user: await fetchUser(),
    posts: await fetchPost(),
}

const store = configureStore({
    reducer: rootReducer,
    preloadedState, // Ін'єкція даних з сервера
})

// Серіалізуємо для клієнта
const html = `
  <script>
    window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState)}
  </script>
`

На клієнті:

client.js
const preloadedState = window.__PRELOADED_STATE__
delete window.__PRELOADED_STATE__

const store = configureStore({
    reducer: rootReducer,
    preloadedState,
})

TypeScript Інтеграція

Типізація RootState та Dispatch

src/store.ts
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './features/counter/counterSlice'

export const store = configureStore({
    reducer: {
        counter: counterReducer,
    },
})

// Експортуємо типи для використання в додатку
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch

Typed Hooks

Створіть типізовані версії хуків:

src/hooks/redux.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from '../store'

// Використовуйте ці хуки замість звичайних useDispatch та useSelector
export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

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

import { useAppSelector, useAppDispatch } from '../hooks/redux'

function Counter() {
    // Автоматичний type inference!
    const count = useAppSelector((state) => state.counter.value)
    const dispatch = useAppDispatch()

    // ...
}
Best Practice: Завжди використовуйте типізовані хуки для автокомпліту та type safety.

Production Оптимізації

Вимкнення Development перевірок

У production build RTK автоматично вимикає:

  • Immutability check middleware
  • Serializability check middleware
  • Redux DevTools

Це відбувається через process.env.NODE_ENV:

// Webpack/Vite автоматично замінять process.env.NODE_ENV на 'production'
// і dead code elimination видалить development код

Ручне налаштування для production

export const store = configureStore({
    reducer: {
        counter: counterReducer,
    },
    middleware: (getDefaultMiddleware) =>
        getDefaultMiddleware({
            // У production вимикаємо перевірки
            immutableCheck: process.env.NODE_ENV !== 'production',
            serializableCheck: process.env.NODE_ENV !== 'production',
        }),
    devTools: process.env.NODE_ENV !== 'production',
})
Увага: Не вимикайте перевірки у development! Вони допомагають виявити проблеми на ранніх етапах.

Підключення до React

Оберніть додаток у <Provider>:

src/index.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux'
import { store } from './store'
import App from './App'

const root = ReactDOM.createRoot(document.getElementById('root'))

root.render(
    <React.StrictMode>
        <Provider store={store}>
            <App />
        </Provider>
    </React.StrictMode>,
)
<Provider> робить store доступним для всіх вкладених компонентів через React Context.

Порівняння: createStore vs configureStore

АспектcreateStore (класичний)configureStore (RTK)
BoilerplateВисокий (10-15 рядків)Низький (3-5 рядків)
DevToolsРучне налаштуванняАвтоматично
Thunk middlewareРучне додаванняIncluded
Immutability checkНемаєАвтоматично (dev)
Serializability checkНемаєАвтоматично (dev)
TypeScript supportПотребує додаткового кодуВідмінний з коробки
Production оптимізаціїРучніАвтоматичні
Рекомендація❌ Застарів✅ Стандарт

Troubleshooting


Висновок

configureStore — це потужна абстракція, яка:

✅ Усуває boilerplate код
✅ Автоматично налаштовує best practices
✅ Захищає від типових помилок
✅ Оптимізує performance у production
✅ Ідеально працює з TypeScript

Золоте правило: Ніколи не використовуйте createStore у нових проєктах. configureStore — єдиний правильний вибір.

Наступні кроки

Тепер, коли store налаштовано, давайте створимо логіку (reducers та actions) за допомогою найпотужнішого інструменту RTK.

👉 Далі: createSlice та магія Immer

Copyright © 2026