Master professional state management by combining Context API and useReducer. Learn how to build a centralized, scalable store for your React dashboard.
Previously in this course, we explored Complex State with useReducer: A React Developer's Guide to handle intricate logic locally, and Introduction to Context API: Avoiding Prop Drilling in React to pass data through the tree. While both are powerful, they are often insufficient on their own for large applications. Today, we combine them to create a robust, centralized architecture for global state management.
In a growing dashboard, you'll find yourself passing "update" functions down through multiple layers of components. This is the classic prop-drilling problem. By pairing useReducer with the Context API, we create a "store" that provides both the current state and a way to update it (the dispatch function) to any component in the tree.
Think of the useReducer as the "brain" (the logic) and the Context as the "nervous system" (the delivery mechanism).
To build this, we need three distinct parts:
Here is how we set this up for our dashboard's global UI settings:
JSXimport React, { createContext, useReducer, useContext } from CE9178">'react'; // 1. Define the initial state and reducer const initialState = { sidebarOpen: true, theme: CE9178">'dark' }; function dashboardReducer(state, action) { switch (action.type) { case CE9178">'TOGGLE_SIDEBAR': return { ...state, sidebarOpen: !state.sidebarOpen }; case CE9178">'SET_THEME': return { ...state, theme: action.payload }; default: return state; } } // 2. Create the Context const DashboardContext = createContext(); // 3. Create the Provider export const DashboardProvider = ({ children }) => { const [state, dispatch] = useReducer(dashboardReducer, initialState); return ( <DashboardContext.Provider value={{ state, dispatch }}> {children} </DashboardContext.Provider> ); }; // 4. Custom hook for easy access export const useDashboard = () => useContext(DashboardContext);
Now that our store is defined, we wrap our application root (or a specific feature boundary) with the DashboardProvider. Because we exported the useDashboard hook, consuming the state becomes trivial and type-safe.
JSX// Inside any component in the tree const SidebarToggle = () => { const { state, dispatch } = useDashboard(); return ( <button onClick={() => dispatch({ type: CE9178">'TOGGLE_SIDEBAR' })}> Sidebar is {state.sidebarOpen ? CE9178">'Open' : CE9178">'Closed'} </button> ); };
This pattern removes the need to pass functions as props. If you need to add a new piece of state—like userProfile—you simply update the reducer and the initial state object. The rest of your application remains untouched.
Refactor your existing dashboard layout to use this pattern.
DashboardProvider that tracks a notifications array.ADD_NOTIFICATION that appends a string to that array.Header component that dispatches this action.NotificationList component elsewhere in your dashboard that consumes the state to display these items.useDashboard() outside of the DashboardProvider, it will return undefined. Always ensure your hook handles this or your Provider wraps the components correctly.By combining useReducer and Context API, we have successfully decoupled the state transition logic from the view layer. We now have a centralized store that allows any component to trigger state changes via dispatch without knowing how those changes occur. This is the cornerstone of professional React application architecture.
Up next: We will dive into creating a dedicated Theme Context to handle dynamic UI styling globally.
Master React state management by understanding when to use local state, custom hooks, or Context. Learn best practices to build maintainable, scalable UIs.
Read moreMaster the useState hook to add interactivity to your React components. Learn how to initialize and update local state to build dynamic user interfaces.
Architecting Global State with Context and Reducer
Introduction to React Router
Dynamic Routing with URL Parameters
Nested Routes and Layouts
Protected Routes for Authenticated Views
Programmatic Navigation
Building the Dashboard Navigation Structure
Asynchronous Data Lifecycle
Caching Strategies with React Query
Mutations and Data Updates
Synchronizing Client and Server State
Integrating Live Data into the Dashboard
Error Handling and Loading UI
Controlled vs Uncontrolled Components
Real-time Form Validation
Schema-based Validation with Zod
Handling Multi-step Forms
Optimizing Form Submissions
Performance Profiling with React DevTools
Refactoring for Scalability
Finalizing Dashboard Data Flow
Deploying the Application
Advanced Hook Composition
Implementing Middleware for State
Advanced Context Patterns
Router Loaders and Data Prefetching
Complex Route Guards
Handling Large Datasets in UI
Testing Hooks and Components
Managing Global Modals
Implementing Keyboard Shortcuts
Optimizing Asset Loading
Internationalization Basics
Managing WebSocket Connections