Redux Toolkit: Simplified State Management for React

Author avatar

Karan Salvi / June 6, 2025

Redux Toolkit: Simplified State Management for React

Redux Toolkit: Simplified State Management for React

Redux Toolkit (RTK) is the official, recommended way to write Redux logic. It helps manage global state in React apps efficiently by reducing boilerplate and providing powerful abstractions.


šŸš€ Why Redux Toolkit?

Traditional Redux requires a lot of setup with action types, action creators, and reducers. RTK simplifies all of this by:

  • āœ… Reducing boilerplate
  • šŸš€ Providing powerful APIs like createSlice, configureStore
  • šŸ”§ Built-in support for Redux Thunk
  • šŸ› ļø Immutable updates via Immer

šŸ”§ Installation

npm install @reduxjs/toolkit react-redux

🧱 Setting Up Redux Toolkit

1. Create a Redux Slice

// features/counter/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;

2. Configure the Store

// app/store.js
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "../features/counter/counterSlice";

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

3. Provide the Store to Your App

// index.js
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { store } from "./app/store";
import App from "./App";

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

šŸ“¦ Using Redux State in Components

1. Reading State with useSelector

import { useSelector } from "react-redux";

function CounterDisplay() {
  const count = useSelector((state) => state.counter.value);
  return <h1>Count: {count}</h1>;
}

2. Dispatching Actions with useDispatch

import { useDispatch } from "react-redux";
import { increment, decrement } from "../features/counter/counterSlice";

function CounterButtons() {
  const dispatch = useDispatch();
  return (
    <div>
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(decrement())}>-</button>
    </div>
  );
}

āš™ļø Async Logic with Thunks

1. Create an Async Thunk

// features/counter/counterSlice.js
import { createAsyncThunk } from "@reduxjs/toolkit";

export const fetchCount = createAsyncThunk("counter/fetchCount", async () => {
  const response = await fetch("/api/count");
  const data = await response.json();
  return data.value;
});

2. Handle in extraReducers

extraReducers: (builder) => {
  builder
    .addCase(fetchCount.pending, (state) => {
      state.status = "loading";
    })
    .addCase(fetchCount.fulfilled, (state, action) => {
      state.status = "succeeded";
      state.value = action.payload;
    })
    .addCase(fetchCount.rejected, (state) => {
      state.status = "failed";
    });
};

🧠 Best Practices

  • Keep slices focused on a single domain (e.g., userSlice, authSlice)
  • Use Immer (built-in) for safe state mutation
  • Avoid prop drilling by lifting state to Redux when needed
  • Use createAsyncThunk for side effects
  • Memoize selectors if needed

šŸ› ļø DevTools & Middleware

Redux Toolkit enables Redux DevTools automatically. You can also add middleware:

const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(customMiddleware),
});

šŸ“ Folder Structure Suggestion

src/
│
ā”œā”€ā”€ app/
│   └── store.js
ā”œā”€ā”€ features/
│   └── counter/
│       ā”œā”€ā”€ counterSlice.js
│       └── CounterComponents.js
└── App.js