"Context + useState" vs "Context + useReducer"


What is State Management, and Why Does It even Matter?
State is the engine of any React app. It decides how components interact, update, and re-render. Poorly managed state often leads to bugs, unnecessary re-renders, and complex debugging. Choosing the right state management pattern ensures your application is scalable, predictable, and easy to maintain.
React’s context API
React’s Context API provides a way to share global state without manually passing props through multiple layers of components. This is especially useful for user authentication, theme switching (light/dark mode thingy), language preferences, shopping carts features.
Instead of prop drilling, Context makes state accessible throughout the component tree in a clean way.
In fact, when I was working at GridFlow on an EV vendor solution management app, we faced these exact decisions about how to handle state. From NFC tariff logic to team access control, we had to decide when useState
was enough and when useReducer
made more sense. Later in this article, I’ll share how our team navigated those choices and kept the architecture simple enough for interns while still building a scalable system.
Understanding useState in React
Let’s quickly go over what useState
and useReducer
are used for (if you’re not already familiar).
useState Hook
The useState
hook lets you declare local state variables in functional components. It’s simple and ideal for managing small pieces of state.
const [count, setCount] = useState(0);
Benefits of Using useState with Context
Easy to understand and implement
Best for small-scale apps
Minimal boilerplate code
Quick setup with fewer moving parts
Limitations of useState in Larger Applications
Can become unwieldy with complex state logic
Multiple
useState
hooks may clutter codeHarder to maintain as state relationships grow
useReducer in React
The useReducer
hook is an alternative to useState
when dealing with complex state logic. It works similarly to Redux by using a reducer function that takes in the current state and an action, then returns a new state.
const [state, dispatch] = useReducer(reducer, initialState);
Benefits of Using useReducer with Context includes
Better for complex state transitions
Centralized logic inside the reducer function
Easier debugging and testing
More predictable state changes
Limitations of useReducer in Certain Scenarios
Boilerplate-heavy compared to
useState
Overkill for simple state management
Beginners may find it harder to grasp
Context + useState
If your app has basic global state (like theme toggling or storing a user ID), useState
with Context is often enough.
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
Context + useReducer
If your app has multiple related states or complex transitions, useReducer shines. Think of shopping carts, form handling, or multi-step workflows.
const CartContext = createContext();
function cartReducer(state, action) {
switch (action.type) {
case "ADD_ITEM":
return { ...state, items: [...state.items, action.payload] };
case "REMOVE_ITEM":
return { ...state, items: state.items.filter(i => i.id !== action.payload) };
default:
return state;
}
}
function CartProvider({ children }) {
const [state, dispatch] = useReducer(cartReducer, { items: [] });
return (
<CartContext.Provider value={{ state, dispatch }}>
{children}
</CartContext.Provider>
);
}
Differences Between Context + useState vs Context + useReducer
useState
: Simple, direct, great for beginners.useReducer
: Structured, but more verbose.For small apps → useState + Context.
For larger apps with complex state → useReducer + Context.
Both work well with React Context, but useReducer
can reduce unnecessary re-renders by grouping state changes.
Which approach should you use for your project? - Depends…
App size: Small → useState, Large → useReducer
State complexity: Simple → useState, Complex → useReducer
Team experience: Beginners → useState, Experienced devs → useReducer
Best Practices
Please don’t over-engineer with useReducer if you don’t need it.
Use Context sparingly to avoid unnecessary re-renders.
Consider external libraries like Zustand or Redux (my favorite) for very large apps.
“The GridFlow incident”: Building an EV Vendor Solution at GridFlow
When I was working at GridFlow, we started building an EV vendor solution management app from scratch. The app needed to handle vendor charger management. customer profiles, NFC tariff management, team management & access control**,** and other features…
The Challenge
We faced a big question:
How do we manage state so that it’s easy for interns to understand, yet scalable enough for advanced features like NFC-based tariff logic and role-based access control?
If we only used useState
, the code risked becoming fragmented with too many local hooks. On the other hand, jumping into Redux or another heavy library would overcomplicate onboarding for interns.
What did we do?
We adopted a hybrid approach:
Context + useState for simple UI states (like toggles, filters, vendor dashboard views).
Context + useReducer for complex workflows (like managing tariffs, updating roles, and handling access control).
Personally, I think this struck the perfect balance, Interns could contribute and use useState whenever they needed to.
The app actually grew smoothly
Completing the tariff management feature became predictable through clear reducer actions. access control feature stayed centralized. the overall architecture remained clean, testable, and scalable.
By choosing wisely between useState
and useReducer
, we ensured the project stayed developer-friendly while still meeting requirements.
My conclusion is…
When deciding between Context + useState vs Context + useReducer, think about the complexity of your state and the size of your app. For small, simple state management, stick with useState
. For larger, more complex state handling, useReducer
provides better structure and maintainability.
“The GridFlow incident” shows how blending both approaches in the same project can deliver a balanced, developer-friendly, and scalable solution.
👉 For a deeper dive into React state management, check out the official React Docs.
Subscribe to my newsletter
Read articles from Shina-kelani Taiwo directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Shina-kelani Taiwo
Shina-kelani Taiwo
hey There I'm Taiwo Shina-Kelani. I'm a frontend developer and a technical writer.. I love meeting new people and exploring new technologies. I am also intrigued by Rust and love Rust language rustacian . I love blogging and tweeting my opinions and skills.