How the useContext Hook Works

By Dave Ceddia Comment

The useContext Hook

There’s a running theme between all these new React Hooks: almost all of them exist to make function components as powerful as class Components.

The useContext hook is a little different though. It just makes things nicer.

In case you haven’t heard of React’s Context API, it’s a way to pass data deeply throughout your app without having to manually pass props down through multiple levels. It can be a good alternative to tools like Redux when all you need to do is pass data around, and you can learn more about Context and how it compares to Redux here.

In this post we’ll look at how useContext makes Context a little easier to consume.

Quick note: Hooks is currently in alpha and not yet ready for production use. The API could still change. I don't recommend rewriting any production apps with hooks at this stage. Leave comments at the Open RFC and check out the official docs and FAQ too.

The Standard Way

The typical way to use the Context API looks like this:

import React from "react";
import ReactDOM from "react-dom";

// Create a Context
const NumberContext = React.createContext();
// It returns an object with 2 values:
// { Provider, Consumer }

function App() {
  // Use the Provider to make a value available to all
  // children and grandchildren
  return (
    <NumberContext.Provider value={42}>
      <div>
        <Display />
      </div>
    </NumberContext.Provider>
  );
}

function Display() {
  // Use the Consumer to grab the value from context
  // Notice this component didn't get any props!
  return (
    <NumberContext.Consumer>
      {value => <div>The answer is {value}.</div>}
    </NumberContext.Consumer>
  );
}

ReactDOM.render(<App />, document.querySelector("#root"));

Here’s the example on CodeSandbox.

See how we get the value inside the Display component? We have to wrap our content in a NumberContext.Consumer and use the render props pattern – passing a function as a child – to retrieve the value and display it.

This works fine, and “render props” is a great pattern for passing dynamic data around, but it does introduce some unnecessary nesting and some cognitive overhead (especially if you’re not used to it).

I cover the Context API in more depth here.

The useContext Way

Let’s rewrite the Display using the new useContext hook and see what it looks like:

// import useContext (or we could write React.useContext)
import React, { useContext } from 'react';

// ...

function Display() {
  const value = useContext(NumberContext);
  return <div>The answer is {value}.</div>;
}

That’s all there is to it. Call useContext, pass in the context object you got from React.createContext, and out pops the value. Easier to read, right?

The only thing you want to watch out for is that you have to pass the whole context object to useContext – not just the consumer! React will warn you if you forget, but try to rememeber, eh?

Nested Consumers

You might have a case where your component needs to receive data from multiple parent contexts, leading to code like this:

function HeaderBar() {
  return (
    <CurrentUser.Consumer>
      {user =>
        <Notifications.Consumer>
          {notifications =>
            <header>
              Welcome back, {user.name}!
              You have {notifications.length} notifications.
            </header>
          }
      }
    </CurrentUser.Consumer>
  );
}

This is an awful lot of nesting just to receive 2 values. Here’s what it can look like with useContext:

function HeaderBar() {
  const user = useContext(CurrentUser);
  const notifications = useContext(Notifications);

  return (
    <header>
      Welcome back, {user.name}!
      You have {notifications.length} notifications.
    </header>
  );
}

Much easier to read.

More Hooks

There are a few more hooks than the ones I was able to cover this week – nice ones too, like useMemo for memoization – that you can learn about from the official Hooks API docs. The official docs are great too – I definitely recommend starting with the intro and reading them through.

And here are the other articles I published during Hooks Week:

I try to publish an article each week (and sometimes even a video). If you want more like this, drop your email in the box 👇

comments powered by Disqus