Skip to content

Instantly share code, notes, and snippets.

@christianalfoni
Created September 6, 2022 06:48
Show Gist options
  • Save christianalfoni/68441838a08f6f9c8a45ebc86ee7cdad to your computer and use it in GitHub Desktop.
Save christianalfoni/68441838a08f6f9c8a45ebc86ee7cdad to your computer and use it in GitHub Desktop.

@codesandbox/context

A more powerful createContext API for React

Install

npm install @codesandbox/context

Difference from React

With React

import { createContext, useContext } from "react";

const context = createContext(null as unknown as Foo);

export const useFoo = () => useContext(context);

export const FooProvider: React.FC = ({ children }) => {
  return <context.Provider value="FOO">{children}</context.Provider>;
};

export const FooConsumer = context.Consumer;

In consuming components:

import { FooProvider, FooConsumer, useFoo } from "./Foo";

const Wrapper = () => {
  return (
    <FooProvider>
      <App />
    </FooProvider>
  );
};

const App = () => {
  const foo = useFoo();

  return <FooConsumer>{(foo) => {}}</FooConsumer>;
};

With @codesandbox/context

import { createContext } from "@codesandbox/context";

export const useFoo = createContext(() => "FOO");

In consuming components:

import { useFoo } from "./hooks/useFoo";

const Wrapper = () => {
  return (
    <useFoo.Provider>
      <App />
    </useFoo.Provider>
  );
};

const App = () => {
  const foo = useFoo();

  return <useFoo.Consumer>{(foo) => {}}</useFoo.Consumer>;
};

What it solves

Even though @codesandbox/context simplifies code, the value is in the conceptual simplification. A context is all about exposing a value on a context and consuming it through a hook. This API expresses exactly that with the createContext function, taking a callback which produces the value to be exposed.

The callback passed to createContext is executed within the returned Provider which means you can use other hooks in it:

const useFoo = createContext(() => useState("FOO"));

This concept also simplifies exposing certain values from one context to an other, optimising reconciliation:

const useProject = createContext(() =>
  useReducer(projectReducer, initialState)
);

const useProjectDispatcher = createContext(() => useProject()[1]);

Dealing with props

Props are passed to the context callback through the provider:

const useFoo = createContext((props: { initialFoo: string }) =>
  useState(props.initialFoo)
);

const SomeComponent = () => {
  return <useFoo.Provider initialFoo="FOO"></useFoo.Provider>;
};

Composing contexts

Often multiple contexts are related and you want to expose them on a single provider:

const useFoo = createContext(() => useState("FOO"));
const useBar = createContext((props: { initialBar: string }) =>
  useState(props.initialBar)
);
const ComposedProvider = composeContexts({ useFoo, useBar });

// Since "useFoo" takes no props, the typing omits it
const SomeComponent = () => {
  return <ComposedProvider useBar={{ initialBar: "BAR" }}></ComposedProvider>;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment