How I Actually Use Redux in a React App (Step-by-Step Guide)
When I first heard about Redux, I honestly thought:
“Why would I need another library just to manage state?”
If you’ve built even a medium-sized React app, you already know the pain:
- Props drilling everywhere
- State living in random components
- Bugs that appear only when the app grows
That’s where Redux slowly starts to make sense.
In this article, I’ll walk you through Redux from a real-world perspective, not theory-heavy documentation. We’ll use Redux Toolkit, because writing Redux the old way in 2026 is just unnecessary pain.
What Problem Redux Solves (In Simple Words)
Redux gives your app:
- One central place to store data
- Predictable updates (no mystery state changes)
- Easy debugging with Redux DevTools
If your app has:
- Authentication state
- Cart data
- User preferences
- Dashboard data shared across pages
Redux is worth it.
Step 1: Install Redux the Right Way
For modern React apps, Redux Toolkit is the official and recommended approach.
npm install @reduxjs/toolkit react-redux
That’s it. No extra setup. No boilerplate horror.
Step 2: Create Your First Slice
A slice is basically:
- State
- Reducers
- Actions all bundled together.
Let’s say we want to manage a counter (simple but powerful example).
// src/redux/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;
},
reset: (state) => {
state.value = 0;
},
},
});
export const { increment, decrement, reset } = counterSlice.actions;
export default counterSlice.reducer;
Why this feels different
Notice something important:
- We’re mutating state directly
- But Redux Toolkit handles immutability behind the scenes using Immer
That alone saves a lot of mental energy.
Step 3: Configure the Redux Store
The store is the brain of Redux.
// src/redux/store.js
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counterSlice";
export const store = configureStore({
reducer: {
counter: counterReducer,
},
});
You can add multiple slices here later (auth, user, theme, etc.).
Step 4: Connect Redux to React
Wrap your app with Provider so Redux becomes available everywhere.
// src/main.jsx or index.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import { Provider } from "react-redux";
import App from "./App";
import { store } from "./redux/store";
ReactDOM.createRoot(document.getElementById("root")).render(
<Provider store={store}>
<App />
</Provider>
);
At this point, Redux is fully wired.
Step 5: Use Redux State in a Component
Now comes the part that makes Redux feel worth it.
import { useSelector, useDispatch } from "react-redux";
import { increment, decrement, reset } from "./redux/counterSlice";
function Counter() {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<h2>Count: {count}</h2>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
<button onClick={() => dispatch(reset())}>Reset</button>
</div>
);
}
export default Counter;
What’s really happening here
useSelector→ reads data from Redux storeuseDispatch→ sends actions to Redux- Redux updates the store
- React re-renders automatically
No props drilling. No hacks.
Step 6: Handling Async Logic (API Calls)
Redux Toolkit makes async logic clean using createAsyncThunk.
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
export const fetchUsers = createAsyncThunk(
"users/fetchUsers",
async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
return res.json();
}
);
const userSlice = createSlice({
name: "users",
initialState: {
data: [],
loading: false,
},
extraReducers: (builder) => {
builder
.addCase(fetchUsers.pending, (state) => {
state.loading = true;
})
.addCase(fetchUsers.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
});
},
});
export default userSlice.reducer;
This pattern is clean, readable, and easy to debug.
Common Mistakes I Made (So You Don’t)
- ❌ Putting everything in Redux
- ❌ Using Redux for local UI state (modals, inputs)
- ❌ Overthinking folder structure
Redux is for shared, long-living state, not everything.
When You Should Use Redux
Use Redux if:
- State is shared across many components
- You want predictable updates
- Debugging matters
Avoid Redux if:
- App is small
- State is mostly local
- Context API already solves your problem
Final Thoughts
Redux is not scary. Bad tutorials make it scary.
With Redux Toolkit, you write:
- Less code
- Cleaner logic
- More maintainable apps
If you’re building real-world React applications, Redux is still very relevant.
