DEV Community

Cover image for 🧩 React Redux Toolkit — A Beginner’s Guide (Modern Redux Explained Clearly)
Vishwark
Vishwark

Posted on

🧩 React Redux Toolkit — A Beginner’s Guide (Modern Redux Explained Clearly)

Learn Redux Toolkit (RTK) — the official, modern way to manage global state in React, with minimal boilerplate and clear structure.


1️⃣ Introduction — What is Redux?

Redux is a predictable state container for JavaScript applications.
It helps manage global state — shared data across multiple components — in a centralized store.

You can use it with React, Vue, Svelte, or even plain JavaScript.


🔹 Why use Redux (over Context API)?

Feature Context API Redux Toolkit
Purpose Pass data down the component tree Manage global, shared state predictably
Scalability Good for small/medium apps Ideal for complex, multi-team apps
Performance Can cause unnecessary re-renders Optimized updates using selectors
Debugging No time travel/debug tools Redux DevTools (history, diffs, time travel)
Structure Unopinionated Enforces clear modular structure

💡 Use Redux when your app needs:

  • Complex state logic (user, theme, notifications, API data)
  • Predictable updates and debugging
  • Collaboration across large teams
  • Middleware or async logic (API handling)

🔹 Core Benefits

  • Single source of truth (one global store)
  • Predictable state updates (reducers)
  • Middleware for side effects
  • DevTools integration
  • Code clarity and scalability

2️⃣ Installation & Setup

Install both Redux Toolkit and React bindings:

npm install @reduxjs/toolkit react-redux
Enter fullscreen mode Exit fullscreen mode
  • @reduxjs/toolkit → Redux + helper APIs + middleware
  • react-redux → Connects Redux to React (Provider, hooks)

🧱 Basic Setup

store.js

import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './counterSlice'

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
})
Enter fullscreen mode Exit fullscreen mode

counterSlice.js

import { createSlice } from '@reduxjs/toolkit'

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: (state) => { state.value += 1 },
    decrement: (state) => { state.value -= 1 },
    incrementByAmount: (state, action) => { state.value += action.payload },
  },
})

export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
Enter fullscreen mode Exit fullscreen mode

main.jsx

import { Provider } from 'react-redux'
import { store } from './store'
import App from './App'

ReactDOM.createRoot(document.getElementById('root')).render(
  <Provider store={store}>
    <App />
  </Provider>
)
Enter fullscreen mode Exit fullscreen mode

3️⃣ Redux Fundamentals

Term Description
State The data managed globally by Redux
Action Object describing what happened
Reducer Pure function that updates the state based on the action
Dispatch Method to send (dispatch) actions to reducers
Slice Modular unit combining state + reducers + actions (via createSlice)

🔹 Actions & Action Creators

An action is a plain object describing a change:

{
  type: 'counter/increment',
  payload: 5 // optional
}
Enter fullscreen mode Exit fullscreen mode
  • type → uniquely identifies the operation
  • payload → carries data to update the state

When you call an action creator (like incrementByAmount(5)),
it returns this action object:

incrementByAmount(5)
// → { type: "counter/incrementByAmount", payload: 5 }
Enter fullscreen mode Exit fullscreen mode

This is then passed to dispatch(action).

So:

dispatch(incrementByAmount(5))
Enter fullscreen mode Exit fullscreen mode

➡ Sends { type, payload } to the store → reducer with matching type runs → state updates → subscribed components re-render.


4️⃣ Accessing State — useSelector & createSelector

🔹 useSelector

Read store data inside a component:

const count = useSelector((state) => state.counter.value)
Enter fullscreen mode Exit fullscreen mode

🔹 Why Selectors Matter

Selectors are crucial for performance:

  • React components re-render whenever the selected part of the store changes.
  • Using specific selectors ensures only relevant components re-render.

🔹 Optimizing with createSelector

When your selector performs calculations (like filtering or summing),
use createSelector from @reduxjs/toolkit for memoization:

import { createSelector } from '@reduxjs/toolkit'

const selectItems = (state) => state.cart.items
export const selectTotalPrice = createSelector(
  [selectItems],
  (items) => items.reduce((sum, i) => sum + i.price, 0)
)
Enter fullscreen mode Exit fullscreen mode

Without memoization:

  • Every render recalculates derived values.
  • All components using it may re-render unnecessarily.

With memoization:

  • Only recomputes when cart.items actually changes.
  • Prevents needless re-renders, improving performance.

5️⃣ Updating the Store — Dispatch Explained

🔹 What is dispatch?

dispatch sends an action object to the Redux store.

const dispatch = useDispatch()
dispatch(incrementByAmount(5))
Enter fullscreen mode Exit fullscreen mode

Here:

  1. incrementByAmount(5) returns { type: 'counter/incrementByAmount', payload: 5 }.
  2. Redux routes it to the matching reducer.
  3. Reducer updates state immutably (via Immer).
  4. Store notifies all subscribed components.
  5. Components that depend on changed state re-render.

Components are subscribed via useSelector — when the selected slice changes, React triggers re-render only for those.


6️⃣ Async Logic — Thunks and Code Clarity

Redux Toolkit includes redux-thunk by default,
allowing async logic inside action creators.

🔹 Basic Example:

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'

export const fetchUser = createAsyncThunk('user/fetch', async (userId) => {
  const res = await fetch(`/api/user/${userId}`)
  return res.json()
})
Enter fullscreen mode Exit fullscreen mode

This thunk returns a function that:

  • Dispatches pending action → { type: "user/fetch/pending" }
  • Waits for async work
  • Dispatches fulfilled or rejected action automatically

🔹 Integrated with Slice:

const userSlice = createSlice({
  name: 'user',
  initialState: { data: null, loading: false },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUser.pending, (state) => { state.loading = true })
      .addCase(fetchUser.fulfilled, (state, action) => {
        state.loading = false
        state.data = action.payload
      })
      .addCase(fetchUser.rejected, (state) => { state.loading = false })
  },
})
Enter fullscreen mode Exit fullscreen mode

🔹 Why Thunks Improve Code Clarity

Without thunks:

dispatch(setLoading(true))
dispatch(fetchUserData())
dispatch(setLoading(false))
Enter fullscreen mode Exit fullscreen mode

With thunks:

  • Logic is centralized inside one async function.
  • Reduces number of dispatch calls.
  • Keeps components clean (only one dispatch(fetchUser())).

7️⃣ Middlewares & DevTools

🔹 Middleware

Middleware intercepts actions before they reach reducers.
Useful for logging, API handling, analytics, etc.

Common ones:

  • redux-thunk (async logic)
  • redux-logger (logs actions)
  • redux-saga (complex side effects)

RTK includes Thunk by default.


🔹 DevTools

Redux DevTools offers:

  • Action timeline (who changed what)
  • State diff inspection
  • Time travel debugging

Enabled automatically with configureStore() — no setup needed.


✅ Summary — Why Redux Toolkit Rocks

Concept Modern Redux Advantage
Setup configureStore() (built-in middleware + devtools)
State Modular slices with createSlice()
Actions Auto-generated with types & creators
Async Simple with createAsyncThunk()
Performance Optimized via selectors & memoization
Debugging Integrated DevTools
Scalability Structured for large codebases

🧭 Final Takeaway

Redux Toolkit makes state management:

  • Predictable (strict flow)
  • Scalable (slices)
  • Readable (less boilerplate)
  • Performant (memoized selectors)

Whether you’re building a small app or an enterprise dashboard —
Redux Toolkit gives you a clean, maintainable way to manage state.


Code sandbox example :


💡 Pro Tip:
If you’re starting fresh with React, skip classic Redux —
go straight to Redux Toolkit (RTK). It’s simpler, faster, and officially recommended.


Top comments (0)