🧭 Introduction
Redux has been one of the most powerful — and most misunderstood — state management tools in React.
Many developers:
- Avoid Redux due to boilerplate
- Misuse Redux for local state
- Don’t understand when Redux is actually needed
Redux Toolkit (RTK) was created to fix these problems.
In this lesson, you will not just learn how Redux Toolkit works —
you’ll learn how professionals think about Redux in modern React apps.
🎯 What You’ll Learn in This Lesson
By the end of this lesson, you will understand:
- Why Redux Toolkit exists
- Core Redux Toolkit concepts
- How Redux Toolkit simplifies Redux
- How async logic works in RTK
- When Redux Toolkit is the right choice
🧠 Mental Model: What Redux Really Is
Redux is not a React feature.
Redux is:
A predictable global state container
It solves problems like:
- Sharing state across distant components
- Keeping state consistent
- Debugging state changes
Redux is NOT meant for:
❌ Local UI state
❌ Temporary form inputs
❌ One-component data
❓ Why Redux Toolkit Was Created
❌ Old Redux Problems
- Too much boilerplate
- Multiple files per feature
- Manual immutable updates
- Complex setup
✅ Redux Toolkit Solutions
- Less boilerplate
- Built-in best practices
- Easier immutable logic
- Standardized patterns
👉 Redux Toolkit is now the official way to use Redux.
🧩 Core Redux Toolkit Building Blocks
Redux Toolkit revolves around 4 key concepts:
1️⃣ Store
2️⃣ Slice
3️⃣ Reducer
4️⃣ Dispatch (Actions)
Let’s break them down.
🏪 Store — The Single Source of Truth
The store holds the entire global state.
import { configureStore } from "@reduxjs/toolkit";
export const store = configureStore({
reducer: {
counter: counterReducer
}
});
Why configureStore is better:
- DevTools enabled automatically
- Middleware setup included
- Cleaner syntax
🧩 Slice — Feature-Based State
A slice represents:
One feature’s state + logic
import { createSlice } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
increment(state) {
state.value += 1;
},
decrement(state) {
state.value -= 1;
}
}
});
export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;
👉 Slice combines:
- State
- Reducers
- Actions
in one place.
🧠 Important Insight: Mutations Are Safe
You might notice this:
state.value += 1;
❓ Isn’t this mutation?
✅ Why This Is Safe
Redux Toolkit uses Immer internally:
- You write “mutating” code
- Immer produces immutable updates
👉 Cleaner code without breaking Redux rules.
🔁 Dispatch — Triggering State Changes
To update state, you dispatch actions:
dispatch(increment());
Flow:
UI → dispatch → reducer → new state → UI update
Redux state is always:
- Predictable
- Traceable
- Centralized
🔗 Connecting Redux to React
Setup Provider
import { Provider } from "react-redux";
import { store } from "./store";
<Provider store={store}>
<App />
</Provider>
Using State
import { useSelector, useDispatch } from "react-redux";
const value = useSelector(state => state.counter.value);
const dispatch = useDispatch();
⚡ Async Logic in Redux Toolkit
Redux Toolkit handles async logic using:
👉 createAsyncThunk
🧩 createAsyncThunk (Core Concept)
Used for:
- API calls
- Async workflows
- Server interactions
Example:
import { createAsyncThunk } from "@reduxjs/toolkit";
export const fetchUsers = createAsyncThunk(
"users/fetch",
async () => {
const res = await fetch("/api/users");
return res.json();
}
);
This creates 3 action states automatically:
pendingfulfilledrejected
🧩 Handling Async States in Slice
const usersSlice = createSlice({
name: "users",
initialState: {
data: [],
loading: false,
error: null
},
reducers: {},
extraReducers: builder => {
builder
.addCase(fetchUsers.pending, state => {
state.loading = true;
})
.addCase(fetchUsers.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(fetchUsers.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message;
});
}
});
👉 Async state handling becomes standardized and readable.
🧠 Redux Toolkit State Shape (Best Practice)
Always include:
{
data,
loading,
error
}
This makes UI predictable and consistent.
🆚 Redux Toolkit vs Context API (Advanced View)
| Scenario | Use |
|---|---|
| Global auth state | Context |
| Theme / language | Context |
| Complex global data | Redux Toolkit |
| API server state | React Query |
| Simple shared values | Context |
👉 Redux is not always the answer — right tool matters.
🚨 Common Redux Toolkit Mistakes
❌ Mistake 1: Putting Everything in Redux
Local state ≠ global state.
❌ Mistake 2: Too Many Slices
One slice per feature, not per component.
❌ Mistake 3: Ignoring Async States
Always handle loading and error.
❌ Mistake 4: Overusing Redux for API Caching
Redux Toolkit ≠ React Query.
🧠 Professional Redux Architecture
Typical structure:
store/
├─ index.js
├─ features/
│ ├─ users/
│ │ ├─ usersSlice.js
│ └─ auth/
│ ├─ authSlice.js
Feature-based, scalable, clean.
🎯 Best Practices (Senior-Level)
✅ Use Redux Toolkit only when needed
✅ Keep slices feature-based
✅ Standardize async state shape
✅ Avoid mixing UI logic in reducers
✅ Prefer clarity over cleverness
❓ FAQs — Redux Toolkit
🔹 Is Redux Toolkit mandatory for Redux?
Yes — it’s the recommended approach.
🔹 Is Redux Toolkit better than Context?
They solve different problems.
🔹 Should I learn Redux without RTK?
No — RTK is Redux today.
🔹 Does Redux Toolkit improve performance?
It improves predictability, not raw speed.
🧠 Quick Recap
✔ Redux Toolkit simplifies Redux
✔ Slices combine logic and state
✔ Immer makes immutable updates easy
✔ createAsyncThunk handles async flows
✔ Use Redux only when it makes sense
🎉 Conclusion
Redux Toolkit is not about complexity —
it’s about structure, predictability, and scale.
Once you understand it:
- Global state becomes manageable
- Debugging becomes easier
- Large apps stop feeling chaotic
This lesson sets the foundation for real-world Redux usage ⚛️🧠
👉 Next Lesson
Lesson 14 — Normalized State & Entity Adapters