React 16.3: How to pass data around using Reacts new Context API

One thing I really love about React is how clean and pure each component can be when done right.

Just how we pass data around by sending props from parent to child components is a simple yet very powerful principle grounded well in functional principles.

Now with that said, in some scenarios it can get messy when we have to explicitly send data through many components to reach where it's needed.

Things like UI themes, user information and so on, are scenarios where the list of props quickly becomes long and the true meaning of the component can be washed away in the verbosity.

Well now, you may ask — "there must be a way around this?" — and yes, with the release of React 16.3 a solution was introduced called the Context API.

What can the Context API do for us?

The Context API can help us spread data around in the component tree without having to manually pass them around as props.

Cool right?

The simplest Context example

To get familiar with all the bits and pieces that the Context API contains, let's start by looking at a simple example.

Say we got a blog that contains posts, which again contains content.

const Content = () => <h1>Headline</h1>;

const Post = () => <Content />;

const Blog = () => <Post />;  

Now say that we want to set a standard color for the blog defined globally in Blog, and we want to use this color in the <h1> tag in the Content component.

Using Context API, we can do this really simple without having to pass props down the component tree.

Creating the context

The first step is to create a context using Reacts createContext.

const ThemeContext = React.createContext();  

Defining the Provider

Now that we got our context defined, we need to add the contexts Provider to our Blog component.

const Blog = () => (  
  <ThemeContext.Provider value="#31B7DA">
    <Post />
  </ThemeContext.Provider>
);

The Provider is a higher order component.

So all we have to do is to wrap everything in the Blog component with our contexts Provider — <ThemeContext.Provider value="#31B7DA">.

The Provider does two things.

First of all it sets the value of the context — which in our case is the color to use.

Secondly, it wraps the part of our component tree that is allowed to read from the context.

Skipping the components between the color definition and usage

We can now skip doing anything in the Post component, since we don't have to explicitly pass the color as props to the Content component — this thanks to our context!

const Post = () => (  
  <Content />
);

Defining the Consumers

The last thing we need to do is to read the value from the context where it's needed.

This is done using a Consumer.

The Consumer is a higher order component that reads from our context.

const Content = () => (  
    <ThemeContext.Consumer>
      { color => <h1 style={{color: color}}>Headline</h1> }
    </ThemeContext.Consumer>
);

The way we add the consumer is by wrapping the content — in our case the h1 tag in Content — with the <ThemeContext.Consumer>, which is our contexts consumer.

As you can see, the Consumer child is a function.

The parameter of the function will be the value of the context, so inside the function we can use the context value freely!

Now if we run this example, the Content component successfully reads and sets the color set by the Provider in the Blog component.

See the Pen React context API Simple by Marius Herring (@mariusherring) on CodePen.

Adding more values and functions to the Context

Now, adding only a string isn't enough in most cases.

If we want a full blown theme for instance, we want to specify multiple colors.

We may also want some dynamic aspect that could change the content of the context.

So let's play this out — say we got a theme with a primary color and a secondary color.

We want them both to be black when initially rendering our blog post.

However, we want to add a button — Make the color pop! — that will change our primary and secondary color to be two sparkly colors.

Let's start by extracting our ThemeContext.Provider into a dedicated component — BlogTheme — that will black box everything that has to do with creating the context value and the provider.

class BlogTheme extends React.Component {

  state = {
    primary: '#000',
    secondary: '#000',
  }

  render() {
    const theme = {
      colors: this.state,
      makeColorPop: () => this.setState({
          primary: '#31B7DA',
          secondary: '#FF7F00',
        })
    }
    return (
    <ThemeContext.Provider value={theme}>
      {this.props.children}
    </ThemeContext.Provider>
  )
  };
};

The first thing to notice is that we no longer send a simple string as the value in the context. We're now sending an object — theme.

This object contains the colors that should be used, which is stored in the state of the component. As you can see the colors are #000 when initializing the state.

The second thing we have in the theme is the function makeColorPop, which changes the state to contain our sparkly colors.

This means our context not only contains the colors we want to spread, but also a function that can be called to change the colors in the context.

Let's now rewrite our Blog, Post and Content component to use these colors and functions.

const Content = () => (  
    <ThemeContext.Consumer>
      { theme => (
        <React.Fragment>
          <h1 style={{color: theme.colors.primary}}>Headline</h1> 
          <p style={{color: theme.colors.secondary}}>content</p>
          <button onClick={theme.makeColorPop}>Make the color pop!</button>
        </React.Fragment>
    )}
    </ThemeContext.Consumer>
);

const Post = () => (  
  <Content />
);

const Blog = () => (  
  <BlogTheme>
    <Post />
  </BlogTheme>
);

As you can see, the example is quite similar to the first one except from two things.

First, the Blog component is cleaner because we extracted the context provider to a new component — BlogTheme.

Second, we now got two colors and a button to change the context values in the Content component.

See the Pen React context API by Marius Herring (@mariusherring) on CodePen.

A final warning

Now, even if the Context API seems to make everything easier, you should use it with care.

Yes, Context API is a perfect way to send global data around — like themes, user information etc — however, we still should strive to use props as much as we can.

Keeping everything in a global context has never been a good idea in any library, framework or language.

But if you need a solution for this kind of scenarios, go ahead and use this new solution. It will keep your components clean and focused on their purpose.

What do you think of the new Context API?