Reducer Context Pattern

Implementing a Global Store with Reducer + Context

Welcome to the first unit of Advanced React State Architecture course. Today, you will set up the backbone of Brew Rush: a single, centralized store powered by useReducer and React Context. This is the foundation that will let you manage complex, game-like state transitions cleanly across the app.

You will create a GameProvider that exposes state and dispatch to any component, plus a couple of hooks that make consuming the store safe and ergonomic.

Building the Context, Provider, and Safe Hooks

Here is the context module that defines the store, provides it to the app, and exports two hooks for usage and selection:

// src/context/GameContext.jsx

import React, { createContext, useContext, useReducer } from 'react';

// Minimal stub for gameReducer and getInitialState to make this file runnable.
// In a real project, these would be imported from '../reducers/gameReducer'
function getInitialState() {
  return {
    ui: { screen: 'menu' }
  };
}
function gameReducer(state, action) {
  switch (action.type) {
    case 'SET_SCREEN':
      return { ...state, ui: { ...state.ui, screen: action.payload } };
    default:
      return state;
  }
}

const GameContext = createContext();

export function GameProvider({ children }) {
  // Initialize reducer with the initial game state
  const [state, dispatch] = useReducer(gameReducer, getInitialState());

  return (
    <GameContext.Provider value={{ state, dispatch }}>
      {children}
    </GameContext.Provider>
  );
}

export function useGameContext() {
  const context = useContext(GameContext);
  if (!context) {
    throw new Error('useGameContext must be used within GameProvider');
  }
  return context;
}

export function useGameSelector(selector) {
  const { state } = useGameContext();
  return selector(state);
}

What this does:

  • createContext sets up a dedicated context for the game store.
  • useReducer initializes state using getInitialState and returns [state, dispatch]. The reducer stub handles a simple SET_SCREEN action.
  • GameProvider makes state and dispatch available to all descendants.
  • useGameContext ensures you only use the store inside the provider; it throws a clear error otherwise.
  • useGameSelector lets you derive a slice of state in components without passing the whole tree around.

Note: In later units, gameReducer and getInitialState will live in a separate reducer module. For now, the stub keeps this file runnable and focused.

Wiring the Provider at the App Root

Wrap your application with the provider so every component can access the global store:

// src/App.jsx
import React from 'react';
import './App.css';
import { GameProvider } from './context/GameContext';

function App() {
  return (
    <GameProvider>
      <div className="app">
        <div className="welcome-container">
          <h1 className="welcome-title">Game Context Provider</h1>
        </div>
      </div>
    </GameProvider>
  );
}

export default App;

What this does:

  • GameProvider wraps the app, enabling any child to call useGameContext or useGameSelector.
  • The UI shows a simple confirmation message; we will add real screens next.

Summary and What’s Next

You now have a centralized store with useReducer and Context, a safe access hook, and a selector helper. This is the backbone that will power Brew Rush’s complex state and screen transitions — cleanly and predictably — without prop drilling.

You are ready to put this pattern to work. Head to the practice section to wire up interactions and see how dispatch-driven state updates flow through your UI.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal