useContext

We use React’s Context API to enable us to easily share stateful data between all levels of an application without having to use repetitious ‘prop-drilling’ at a component level.

This works best with data that is global by definition, such as theme parameters or access authorisation. Context should not be used when standard and proximate compositional methods and prop-passing are sufficient.

Providers and consumers

The Context API preceded the introduction of hooks in React 16.8 and it still underpins context management in hooks, although the syntax is simplified.

The process is as follows:

  • Initiate a context
  • Create a parent Provider component that owns the data that will be passed down
  • Create child Consumer components that receive the data of the parent.

We will demonstrate using a set of styles as the context that we want to pass around our app.

const style = {
  border: "2px solid dodgerblue",
  background: "lightblue",
  color: "dodgerblue",
};

Le’s initiate a context:

const StyleContext = React.createContext();

Now that we have our data and have initialized a context, we can apply it to our app components:

<StyleContext.Provider value={style}>...</StyleContext.Provider>

This is the parent element of the context environment. This stores the contextual data as a prop. Next we need to make a component that acts as the consumer of this data. Let’s create this component and call it Child for simplicity:

const Child = () => {
  return (
    <StyleContext.Consumer>
      {(value) => (
        <div style={value}>
          <p>
            I'm a <b>child</b>. I <b>consume</b> of the data my parent provides.
          </p>
        </div>
      )}
    </StyleContext.Consumer>
  );
};

The contents of this component are wrapped in tags that reference the specific context (StyleContext) and the role that the component plays: Consumer.

We are taking the value prop owned by StyleContext.Provider and passing it to the wrapping component of Child as a style tag. This means that Child will display these styles.

The final step is just to insert our <Child /> components beneath the parent component, e.g.:

<StyleContext.Provider value={style}>
  <Child />
  <Child />
</StyleContext.Provider>

useContext hook

The hook removes the need to explicitly declare Provider and Consumer components; their role becomes more implicit.

To demonstrate let’s use a different example. We are going to have a single integer as the data context, 42.

First invoke and intialize the Context API, just as before:

const NumberContext = React.createContext(42);

Next, we just need to create our provider component. We don’t have to worry about a consumer since this is handled implicitly by the invocation of the hook

const Context = () => {
  const data = useContext(NumberContext);
  return <Typography>{data}</Typography>;
};

Then, in our code we just insert the Context component:

<Context>...</Context>

Updating context state

In the examples above we have only been consuming state that is owned by the provider however in most scenarios you will also want to update the state from a consumer. This is best achieved by combining useContext with a reducer and is detailed in Application state management.