Global state

2 snippets in React

REReact

Context + Reducer Pattern

RE · Patterns
syntax
const StateCtx = createContext(null);
const DispatchCtx = createContext(null);

function Provider({ children }) {
  const [state, dispatch] = useReducer(reducer, init);
  return (
    <StateCtx.Provider value={state}>
      <DispatchCtx.Provider value={dispatch}>
        {children}
      </DispatchCtx.Provider>
    </StateCtx.Provider>
  );
}
example
const TodoStateContext = createContext(null);
const TodoDispatchContext = createContext(null);

function todoReducer(state, action) {
  switch (action.type) {
    case 'ADD':
      return [...state, { id: crypto.randomUUID(), text: action.text, done: false }];
    case 'TOGGLE':
      return state.map(t =>
        t.id === action.id ? { ...t, done: !t.done } : t
      );
    case 'DELETE':
      return state.filter(t => t.id !== action.id);
    default:
      throw new Error(`Unhandled: ${action.type}`);
  }
}

function TodoProvider({ children }) {
  const [todos, dispatch] = useReducer(todoReducer, []);
  return (
    <TodoStateContext.Provider value={todos}>
      <TodoDispatchContext.Provider value={dispatch}>
        {children}
      </TodoDispatchContext.Provider>
    </TodoStateContext.Provider>
  );
}

function useTodos() {
  return useContext(TodoStateContext);
}
function useTodoDispatch() {
  return useContext(TodoDispatchContext);
}

Note This pattern provides Redux-like state management with built-in React APIs. Splitting state and dispatch into separate contexts prevents components that only dispatch actions from re-rendering when state changes.

Context for Global State

RE · State Management
syntax
// Create -> Provide -> Consume
const Ctx = createContext(defaultValue);
<Ctx.Provider value={value}>...
const value = useContext(Ctx);
example
// Locale context for internationalization
const LocaleContext = createContext('en');

function LocaleProvider({ children }) {
  const [locale, setLocale] = useState('en');

  const translations = useMemo(
    () => loadTranslations(locale),
    [locale]
  );

  const value = useMemo(
    () => ({ locale, setLocale, t: (key) => translations[key] || key }),
    [locale, translations]
  );

  return (
    <LocaleContext.Provider value={value}>
      {children}
    </LocaleContext.Provider>
  );
}

function useLocale() {
  return useContext(LocaleContext);
}

// Usage in any component
function WelcomeBanner() {
  const { t, locale } = useLocale();
  return <h1>{t('welcome_message')} ({locale})</h1>;
}

Note Context is ideal for low-frequency updates like theme, locale, and auth. For high-frequency updates (e.g., mouse position), context can cause excessive re-renders -- consider useSyncExternalStore or a dedicated state library.