Global State Management in React: Context API + useReducer + useState
- coding z2m
- Apr 1
- 4 min read
Updated: Apr 4

Introduction
When building modern React applications, managing state efficiently across multiple components is crucial. While useState works well for local component state, managing complex state logic across multiple components can be challenging.
Why Use React Context + useReducer + useState Together?
Context API eliminates prop drilling by providing a global state accessible from any component. Using useReducer (For Complex State Logic)
Now, if you have more complex state management—for example, if you want to handle different types of actions (like adding, removing, and updating multiple resources or categories)—then useReducer can be very helpful.
It is an alternative to useState for managing state with multiple related actions.
It follows the Redux-style reducer pattern (i.e., state → action → new state).
Ideal for handling complex state transitions (e.g., adding, removing, updating multiple resources).
const [state, dispatch] = useReducer(reducerFunction, initialState);
state → The current state.
dispatch → Function to trigger state updates.
reducerFunction → Function that defines how state changes based on actions.
initialState → The starting state.
useState is still useful for managing simple, independent state (e.g., toggling dark mode).
By combining these three hooks, we can create a scalable, maintainable, and performant global state management solution for React apps.
What Will You Learn?
How to set up a global state with React Context API
When to use useReducer instead of multiple useState hooks
How to wrap your entire app with a Context Provider
How to use useState alongside useReducer for independent state
Let’s dive into the implementation and see how it all works together!
Example:
Setting up a global state management system for the app using Context API and useReducer, combined with useState for theme toggling. Initial State (Global State) A state object containing two properties.
const initialState = {
resources: [],
categories: ["Articles", "Videos", "Courses"],
};
resources → Stores saved learning materials (articles, videos, courses).
categories → Predefined learning resource types.
Reducer Function (useReducer) This is a Reducer Function designed to work with React's useReducer hook. The function takes two parameters:
state – the current state
action – an object with a type (determining what change to make) and possibly a payload (data used for the update).
How It Works (Case by Case Analysis)
ADD_RESOURCE Case
Action Type: "ADD_RESOURCE"
Effect: Adds a new resource (action.payload) to the resources array.
return { ...state, resources: [...state.resources, action.payload] };
Creates a new state object ({ ...state } to ensure immutability).
Expands the resources array ([...state.resources]), then appends action.payload.
const appReducer = (state, action) => {
switch (action.type) {
case "ADD_RESOURCE":
return { ...state, resources: [...state.resources, action.payload] };
case "REMOVE_RESOURCE":
return {
...state,
resources: state.resources.filter((res) => res.id !== action.payload),
};
case "ADD_CATEGORY":
return { ...state, categories: [...state.categories, action.payload] };
default:
return state;
}
};
Handles complex state changes with useReducer
ADD_RESOURCE → Adds a new learning resource to the resources array
REMOVE_RESOURCE → Removes a resource based on its id
ADD_CATEGORY → Adds a new category
This is more efficient than multiple useState hooks for managing complex state logic.
Creating the Context
const AppContext = createContext();
Creates a global context (AppContext) to store and share state across the app
Context Provider (AppProvider)
This is a React Context Provider (AppProvider) that wraps the application and manages global state using useReducer and useState.
What does this do?
This is a Context Provider Component that takes children (other components wrapped inside it)
Uses useReducer to manage state with appReducer and initialState.
dispatch function that sends an action object to appReducer to update the state.
Creates a Context Provider (AppContext.Provider).
It stores and provides the global state (state, dispatch, isDarkMode, toggleTheme) to all child components.
Makes state, dispatch, isDarkMode, and toggleTheme available globally to all children components.
Wraps children inside a div that applies dark or light theme styles.
export const AppProvider = ({ children }) => {
const [state, dispatch] = useReducer(appReducer, initialState);
const [isDarkMode, setIsDarkMode] = useState(false);
// Toggle theme
const toggleTheme = () => setIsDarkMode((prev) => !prev);
return (
<AppContext.Provider value={{ state, dispatch, isDarkMode, toggleTheme }}>
<div className={isDarkMode ? "dark bg-gray-900 text-white" : "light"}>
{children}
</div>
</AppContext.Provider>
);
};
Custom Hook for Easier Access
export const useAppContext = () => useContext(AppContext);
This simplifies context usage in components.
How is this Used in the App?
Wrap the App Component (in main.jsx) When you wrap the entire <App /> component with <AppProvider>, you are:
Wrapping all children of App inside AppContext.Provider.
Providing global access to the context values (state, dispatch, isDarkMode, toggleTheme).
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { AppProvider } from "./context/AppContext";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<AppProvider>
<App />
</AppProvider>
</React.StrictMode>
);
How Do Components Use This Context? Example: Accessing State in ResourceForm.jsx
import { useAppContext } from "../context/AppContext";
const ResourceForm = () => {
const { dispatch } = useAppContext();
const addResource = () => {
dispatch({
type: "ADD_RESOURCE",
payload: { id: Date.now(), title: "New Resource", type: "Articles" },
});
};
return <button onClick={addResource}>Add Resource</button>;
};
Directly dispatches an action (ADD_RESOURCE) without passing props manually!
Summary
Uses useReducer for complex state (resources & categories). Uses useContext to make global state available across the app. Manages Dark Mode with useState. Encapsulates logic inside AppProvider for better structure. Provides useAppContext() custom hook for easy state access.
This makes the app scalable, efficient, and easy to maintain!
Kommentare