Redux in React – Store, Reducers, and Actions (Step-by-Step Guide)
Redux is a predictable state container for JavaScript applications.
It is commonly used with React to manage global state, making your app more scalable and maintainable.
Key concepts in Redux:
Store – Holds the application state
Reducers – Functions that update state based on actions
Actions – Descriptions of what should happen in the state
Redux enforces a unidirectional data flow, keeping your state predictable and easier to debug.
Redux Store
The store is the single source of truth for your app’s state.
It holds the current state object and provides methods to read state, dispatch actions, and subscribe to updates.
Creating a Redux store:
import { createStore } from "redux";
import { counterReducer } from "./counterReducer";
const store = createStore(counterReducer);
export default store;
Store Methods:
| Method | Purpose |
|---|---|
getState() | Returns current state |
dispatch(action) | Updates state by sending an action |
subscribe(listener) | Listen for state changes |
Actions
Actions are plain JavaScript objects describing what happened.
Structure of an action:
{ type: "INCREMENT", payload: 1 } type→ required, describes the actionpayload→ optional, contains data for the reducer
Example Actions:
export const increment = () => ({ type: "INCREMENT" });
export const decrement = () => ({ type: "DECREMENT" });
export const reset = () => ({ type: "RESET" });
Reducers
Reducers are pure functions that take the current state and an action, and return new state.
Rules for reducers:
Pure function (no side effects)
Returns a new state object, does not mutate the existing state
Handles different action types
Example Counter Reducer:
export const counterReducer = (state = { count: 0 }, action) => {
switch (action.type) {
case "INCREMENT":
return { count: state.count + 1 };
case "DECREMENT":
return { count: state.count - 1 };
case "RESET":
return { count: 0 };
default:
return state;
}
};
Example: Counter App with Redux
Step 1: Install Redux
npm install redux react-redux
Step 2: Create Store and Reducer
// counterReducer.js
export const counterReducer = (state = { count: 0 }, action) => {
switch (action.type) {
case "INCREMENT": return { count: state.count + 1 };
case "DECREMENT": return { count: state.count - 1 };
case "RESET": return { count: 0 };
default: return state;
}
};
// counterReducer.js
export const counterReducer = (state = { count: 0 }, action) => {
switch (action.type) {
case "INCREMENT": return { count: state.count + 1 };
case "DECREMENT": return { count: state.count - 1 };
case "RESET": return { count: 0 };
default: return state;
}
};
Step 3: Connect Redux to React
import React from "react";
import ReactDOM from "react-dom";
import { Provider, useDispatch, useSelector } from "react-redux";
import { store } from "./store";
function Counter() {
const count = useSelector((state) => state.count);
const dispatch = useDispatch();
return (
<div>
<h2>Count: {count}</h2>
<button onClick={() => dispatch({ type: "INCREMENT" })}>+</button>
<button onClick={() => dispatch({ type: "DECREMENT" })}>-</button>
<button onClick={() => dispatch({ type: "RESET" })}>Reset</button>
</div>
);
}
ReactDOM.render(
<Provider store={store}>
<Counter />
</Provider>,
document.getElementById("root")
);
How it works:
storeholds the statedispatchsends actions to the reduceruseSelectorreads state from the storeuseDispatchsends actions to update state
Best Practices
Keep reducers pure and small
Use action creators for consistency
Split large state into multiple reducers using
combineReducersUse Redux DevTools for debugging state changes
Avoid storing derived data in the store; compute it in selectors