When I first built small React apps, useState was enough. But as projects grew, managing state across multiple components became a nightmare. That’s when I explored Redux — and later, the Context API. Both solved problems, but in different ways.
Here’s what I learned while scaling React apps.
Perfect for small apps:
function TodoApp() {
const [todos, setTodos] = React.useState([]);
const [filter, setFilter] = React.useState("all");
// works fine here, but gets messy in big apps
}
Problem: when multiple components need the same data (e.g., todos, user, theme), props start drilling down everywhere.
Redux introduced a single source of truth.
// reducer.js
const initialState = { count: 0 };
function counterReducer(state = initialState, action) {
switch (action.type) {
case "INCREMENT":
return { count: state.count + 1 };
default:
return state;
}
}
export default counterReducer;
// App.js
import { useSelector, useDispatch } from "react-redux";
function Counter() {
const count = useSelector((state) => state.count);
const dispatch = useDispatch();
return (
{count}
);
}
Pros:
Scales well for large apps
Predictable with strict rules
Excellent dev tools
Cons:
Boilerplate-heavy
Can feel overkill for smaller projects
React introduced Context API to avoid prop drilling.
const ThemeContext = React.createContext();
function App() {
return (
);
}
function Toolbar() {
const theme = React.useContext(ThemeContext);
return Theme is {theme};
}
Pros:
Simple for small-to-medium apps
No external libraries needed
Great for themes, authentication, language settings
Cons:
Can cause unnecessary re-renders if misused
Not ideal for very complex state logic
Start Simple → Context API is often enough.
Use Redux (or newer tools like Zustand, Recoil) when state becomes huge.
Mix and Match → In some apps, I used Context for authentication & theme, and Redux for complex business logic.
Don’t Over-engineer → Many devs add Redux too early, making projects harder.
State management isn’t about the tool — it’s about solving the complexity of shared data.
Small projects? useState + Context.
Medium projects? Context + custom hooks.
Large projects? Redux (or similar libraries).
The lesson I learned: pick the simplest solution until the complexity forces you to scale up.