Welcome back to the React Mastery Series!
In the previous article, we explored TypeScript with React and learned how static typing improves code quality, developer productivity, and application reliability.
Today, we're diving into one of the most requested topics in React interviews and one of the most widely used state management solutions in enterprise applications:
Redux Toolkit (RTK)
If you've ever wondered:
- Why do we need Redux when React already has Context API?
- What problems does Redux solve?
- Why is Redux Toolkit preferred over traditional Redux?
This article answers all of those questions.
Why Do We Need Redux?
Imagine you're building an enterprise banking application.
It contains:
- Login
- Dashboard
- Accounts
- Transactions
- Beneficiaries
- Profile
- Notifications
Many screens need the same data:
- Logged-in user
- Authentication token
- Customer profile
- Theme
- Permissions
Without centralized state:
Component A
|
Component B
|
Component C
|
Component D
Each component manages its own copy of the data.
Problems:
- Duplicate API calls
- Inconsistent state
- Difficult debugging
- Complex data flow
Context API vs Redux
Many developers ask:
"Why not just use Context API?"
Context works well for:
- Theme
- Authentication
- Language
- User preferences
However, for applications with:
- Frequent state updates
- Large datasets
- Complex business logic
- Multiple developers
Redux provides better scalability and tooling.
| Context API | Redux Toolkit |
|---|---|
| Good for simple shared state | Designed for complex state |
| Basic state sharing | Predictable state management |
| Limited debugging | Excellent DevTools support |
| Minimal boilerplate | Simplified with RTK |
What is Redux?
Redux is a predictable state management library.
It stores application state in one central location called:
Store
Instead of multiple components managing separate state:
Header
Dashboard
Profile
Sidebar
Everything reads from one source.
Store
|
--------------------------
| | | |
Header Dashboard Profile Sidebar
Core Redux Concepts
Redux is built around four ideas.
Store
↓
Action
↓
Reducer
↓
Updated State
Let's understand each one.
What is a Store?
The Store is the central container for your application's state.
Example:
Store
├── auth
├── user
├── transactions
├── accounts
└── settings
Every component reads data from the Store.
What is an Action?
An Action describes what happened.
Example:
{
type: "LOGIN"
}
Another example:
{
type: "ADD_TRANSACTION",
payload: {
amount: 500
}
}
Notice:
Actions never update state directly.
They simply describe an event.
What is a Reducer?
A Reducer decides how state changes.
Example:
Current State
+
Action
↓
New State
Reducers must:
- Be pure functions
- Never mutate existing state
- Always return new state
Traditional Redux vs Redux Toolkit
Older Redux required:
- Action creators
- Constants
- Reducers
- Store configuration
- Boilerplate code
Example project:
actions.js
constants.js
reducers.js
store.js
Redux Toolkit removes most of this boilerplate.
Installing Redux Toolkit
npm install @reduxjs/toolkit react-redux
Packages:
@reduxjs/toolkitreact-redux
These are all you need for most applications.
Creating the Store
import { configureStore } from "@reduxjs/toolkit";
import authReducer from "./authSlice";
export const store = configureStore({
reducer: {
auth: authReducer
}
});
configureStore() automatically enables useful defaults like Redux DevTools and middleware.
What is a Slice?
A Slice groups:
- State
- Reducers
- Actions
into one file.
Example:
authSlice.ts
├── Initial State
├── Reducers
└── Generated Actions
This is the biggest improvement introduced by Redux Toolkit.
Creating Your First Slice
import { createSlice } from "@reduxjs/toolkit";
const initialState = { user: null };
const authSlice = createSlice({
name: "auth",
initialState,
reducers: {
login(state, action) {
state.user = action.payload;
},
logout(state) {
state.user = null;
}
}
});
export const { login, logout } = authSlice.actions;
export default authSlice.reducer;
Notice:
We appear to modify state directly.
Redux Toolkit uses Immer internally, so immutable updates happen automatically.
Providing the Store
Wrap the application.
import { Provider } from "react-redux";
<Provider store={store}>
<App />
</Provider>
Now every component can access Redux state.
Reading State
Use:
useSelector()
Example:
import { useSelector } from "react-redux";
const user = useSelector(state => state.auth.user);
Flow:
Store
↓
useSelector()
↓
Component
Updating State
Use:
useDispatch()
Example:
import { useDispatch } from "react-redux";
const dispatch = useDispatch();
dispatch(login({ id: 1, name: "Siva"}));
Flow:
Button Click
↓
dispatch()
↓
Reducer
↓
Store Updated
↓
Component Re-renders
Enterprise Authentication Flow
Login Form
↓
Authentication API
↓
dispatch(login())
↓
Redux Store
↓
Navbar Updates
↓
Dashboard Updates
↓
Profile Updates
Every component immediately receives the updated user information.
Async API Calls with createAsyncThunk
Most applications fetch data from APIs.
Redux Toolkit provides:
createAsyncThunk()
Example:
export const fetchUsers = createAsyncThunk("users/fetch", async () => {
const response = await fetch("/api/users");
return response.json();
});
Redux automatically generates:
- Pending
- Fulfilled
- Rejected
states.
Handling Async States
API Request
↓
Pending
↓
Success OR Failure
Typical state:
{
users: [],
loading: false,
error: null
}
Perfect for enterprise applications.
Folder Structure
A scalable Redux Toolkit project:
src
├── app
│ └── store.ts
│
├── features
│ ├── auth
│ │ └── authSlice.ts
│ │
│ ├── users
│ │ └── userSlice.ts
│ │
│ └── transactions
│ └── transactionSlice.ts
Each feature owns its own Redux logic.
Real-World Banking Example
Imagine a customer logs in.
Login
↓
dispatch(login())
↓
Store Updated
↓
Header
Sidebar
Dashboard
Profile
Notifications
Every screen instantly reflects the new authentication state.
Redux DevTools
One of Redux's biggest advantages is debugging.
You can inspect:
- Every action
- Previous state
- New state
- Action payload
- State history
Example:
LOGIN
↓
FETCH_ACCOUNTS
↓
ADD_BENEFICIARY
↓
LOGOUT
This makes debugging much easier than scattered component state.
Common Mistakes
1. Putting Everything in Redux
Not all state belongs in the Store.
Avoid storing:
- Modal visibility
- Form input values
- Tooltip state
- Local UI toggles
Use useState() for component-specific state.
2. Mutating State Outside Redux Toolkit
Incorrect:
state.user.name = "John";
outside a reducer.
Always update state through Redux actions.
3. Creating One Huge Slice
Avoid:
appSlice
containing:
- User
- Products
- Orders
- Notifications
- Theme
Instead:
authSlice
userSlice
transactionSlice
themeSlice
Keep slices focused.
Best Practices
- Organize Redux by feature.
- Keep slices small and focused.
- Use
createAsyncThunk()for API requests. - Keep UI state local.
- Use Redux DevTools during development.
- Prefer Redux Toolkit over traditional Redux.
Key Takeaways
Today, we learned:
✅ Redux provides centralized state management.
✅ Redux Toolkit significantly reduces Redux boilerplate.
✅ A Slice combines state, reducers, and actions.
✅ useSelector() reads data from the Store.
✅ useDispatch() sends actions to update state.
✅ createAsyncThunk() simplifies asynchronous API calls.
✅ Redux Toolkit is the preferred Redux approach for modern React applications.
Coming Next 🚀
In Day 23, we will explore:
API Integration in React – Fetch, Axios, Error Handling & Best Practices
We will learn:
- Fetch API vs Axios
- GET, POST, PUT, DELETE requests
- Request interceptors
- Response interceptors
- Authentication tokens
- Global error handling
- Loading and retry strategies
- Enterprise API architecture
By the end of the next article, you'll know how production React applications communicate securely and efficiently with backend services.
Happy Coding! 🚀
Top comments (0)