Sophie Au

Software Developer, Web Designer, Tea Enthusiast

React Context

26 April 2020

React Context is the easiest and most comfortable way to handle global state (or global anything really). It's essentially like Redux but I don't hate it. Much less boilerplate and it Just Works™.

You'll always want to create a context in a separate file and pass the components to wrap in as children. This has multiple reasons:

  1. most importantly: if you pass the app in as a children prop it won't rerender on every context value change. Every non-prop Provider child and every context consumer will rerender when context values change.
  2. pulling out all Context functionality allows you to create a clean api. The only things you'll want to export are the ProviderComponent and the useContext function. Both of them should wrap the react api.
interface ContextType {
  something: Something | null;
  setAssetType: Dispatch<Something | null>;
}

const SomethingContext = React.createContext<ContextType>({
  something: null,
  setSomething: () => {}
});

const reducer = (oldSomething: Something, newInput: Something) => {
  const newSomething = doSomething(newInput, oldSomething)

  return newSomething
};

const SomethingContextProvider: React.FC = ({ children }) => {
  const [something, setSomething] = React.useReducer(reducer, null);

  return (
    <SomethingContext.Provider value={{ something, setSomething }}>
      {children}
    </SomethingContext.Provider>
  );
};

const useSomethingContext = () => useContext(SomethingContext);

export { SomethingContextProvider, useSomethingContext };

And then in your app root file, you use the context like so:

const App: React.FC = () => (
  <SomethingContextProvider>
    <AppContainer />
  </SomethingContextProvider>
);

And finally, this is how you actually use the context in a component:

const Consumer: React.FC = () => {
  const { something, useSomethingContext } = useSomethingContext();

  return (
    <div>
      {`Current Something: ${something}`}
      <button onClick={() => useSomethingContext(newSomethingValue)} />
    </div>
  );
};