Skip to content

Instantly share code, notes, and snippets.

@modernserf
Last active March 24, 2019 17:46
Show Gist options
  • Save modernserf/08b7f395b1b9f5d7fd338ff8da2e129e to your computer and use it in GitHub Desktop.
Save modernserf/08b7f395b1b9f5d7fd338ff8da2e129e to your computer and use it in GitHub Desktop.
The React Context API in an alternate universe

The React Context API in an alternate universe

Overview

Context is "just" a prop with special behavior, like children or key. It implicitly propagates from elements to their children, unless it is explicitly overridden.

Context can be provided as a prop (to elements) or via the children object's withContext method. Context can be consumed as a prop (to components) or by using a function in children.

This component:

const Container = ({ children }) => (
  <section>
    <Header>
      <h1>Hello, World</h1>
      <Nav />
    </Header>
    {children}
    <Footer/>
  </section>
)

is equivalent to:

const Container = ({ children, context }) => (
  <section>
    <Header context={context}>
      <h1>Hello, World</h1>
      {(headerContext) => <Nav context={headerContext} />}
    </Header>
    {children.withContext(context)}
    <Footer context={context}/>
  </section>
)

Note that HTML elements and text nodes do not interact with context.

API Differences

Context

Context is an object with the following methods:

update: create a new context with an updated value

context.update(
  key: string | symbol,
  update: (previousValue) => nextValue
) => Context

get: read a value from context, or get its default value

context.get(
  key: string | symbol,
  defaultValue?: any 
) => any

Context can also be consumed via a `useContext` hook:

```js
const value = useContext(key, defaultValue)

Context can be provided to elements via the context prop.

<Foo context={context}/>

If an element is not explicitly provided a context prop, it will receive the context its parent element passes to its children.

Passing a non-Context value as a context prop is an error.

Children

Children is an object with the following methods:

withContext: create a new children object with an updated context

children.withContext(Context) => Children

Calling withContext with a non-Context value is an error.

Regardless of what type of value is passed as the children prop, the component will always receive an opaque Children object.

If a function is used in children, that function is called with the parent context as its argument:

<Container>
  {(context) => <Foo value={context.use(key)} />}
</Container>

Rough equivalents to real-world React

function createContext (defaultValue) {
  const key = Symbol()

  // <Context.Provider value={value}>{...}</Provider>
  const Provider = ({ value, children, context }) =>
    children.withContext(context.update(key, () => value))

  // <Context.Consumer render={(value) => ...} />
  const Consumer = ({ render, context }) =>
    render(context.get(key, defaultValue))

  // const value = Context.use()
  function use () {
    return useContext(key, defaultValue)
  }

  return { use, Provider, Consumer }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment