Skip to content

Instantly share code, notes, and snippets.

@TimothyDJewell
Last active July 11, 2023 00:17
Show Gist options
  • Save TimothyDJewell/48a5b9ce421e0bbd3ff39ab620d188aa to your computer and use it in GitHub Desktop.
Save TimothyDJewell/48a5b9ce421e0bbd3ff39ab620d188aa to your computer and use it in GitHub Desktop.
React reducer context provider

While reading through scaling up with reducer and context from the react docs, the docs took the user through a series of steps to transfer reading state and dispatch from the props to the context, resulting in set of abstractions like:

const TasksContext = createContext(null);

const TasksDispatchContext = createContext(null);

export function TasksProvider({ children }) {
  const [tasks, dispatch] = useReducer(
    tasksReducer,
    initialTasks
  );

  return (
    <TasksContext.Provider value={tasks}>
      <TasksDispatchContext.Provider value={dispatch}>
        {children}
      </TasksDispatchContext.Provider>
    </TasksContext.Provider>
  );
}

export function useTasks() {
  return useContext(TasksContext);
}

export function useTasksDispatch() {
  return useContext(TasksDispatchContext);
}

The idea here is that the developer will wrap the relevant top-part of their application in <TasksProvider>...</TasksProvider>, which will set up all lower levels of the application to have the relevant context. Then useTasks() and useTasksDispatch() are available as hooks to get the state and dispatch in any child context. This works great, but it's a lot of boilerplate, so I figure we can extract the work here into something more like:

const createProvider = (reducer, initialState) => {
  const StateContext = createContext(initialState);
  const DispatchContext = createContext(null);
  const Provider = ({ children }) => {
    const [state, dispatch] = useReducer(reducer, initialState);
    return (
      <StateContext.Provider value={state}>
        <DispatchContext.Provider value={dispatch}>
          {children}
        </DispatchContext.Provider>
      </StateContext.Provider>
    );
  };
  const useState = () => useContext(StateContext);
  const useDispatch = () => useContext(DispatchContext);
  return { Provider, useState, useDispatch };
};

This gets used to make generally the same effect like:

export const {
  Provider: TasksProvider,
  useState: useTasks,
  useDispatch: useTasksDispatch,
} = createProvider(tasksReducer, initialTasks);

This abstraction seems to kill pretty much all the boilerplate, and I am a little puzzled why I did not see it mentioned as something in the react standard lib or immediately find an npm package to make this utility. Maybe this is something provided via the react-redux package or the like.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment