Understanding React Context API

React Context API is a feature that provides a way to pass data down through the component tree without needing to pass props manually between components. The value of React Context provided by one component can be accessed by other components by consuming the context.

The data you shared through Context API can be considered as “global” for a tree of React components. The Context API can be used to share the currently authenticated user, selected theme or preferred language.

Creating context is as simple as calling the React.createContext() function and assign it into a variable. Just like useState hook, you can put a default data into as the first argument of the function:

import React from 'react';

// defaults to 'en'
const LanguageContext = React.createContext('en')

Now we have the LanguageContext available for use. This also gives you the LanguageContext.Provider and LanguageContext.Consumer component. Let’s learn how to provide data value into the context first.

Providing context

The value provided into context is usually obtained from state, so that when the state change, the context value also change. You need to wrap your React component with the Provider component. Here’s an example:

import React from 'react'

const LanguageContext = React.createContext('en')

function App() {
  const language = 'fr'

  return (
    <LanguageContext.Provider value={language}>
      <Hello />
    </LanguageContext.Provider>
  )
}

Now both Hello and all components under it can access the value of LanguageContext. There are many ways to extract the value of a context depending on your component type (function or class). Let’s learn how to extract content value in function components first.

Consuming context in function components

Function components can grab the value of context by using the useContext hook. The hook accepts a context object — which is returned by React.createContext() — and returns the value of that context.

Here’s the example:

function Hello() {
  const language = useContext(LanguageContext)

  if (language === "fr") {
    return <h1>Bonjour!</h1>
  }
  return <h1>Hello!</h1>
}

In most application requirements, you will need the context value to change over time. You can use the state to change the value of context like this:

import React, { useState, useContext } from "react"

const LanguageContext = React.createContext()

function App() {
  const [language, setLanguage] = useState("en");

  const changeLanguage = () => {
    if (language === "en") {
      setLanguage("fr")
    } else {
      setLanguage("en")
    }
  };

  return (
    <LanguageContext.Provider value={language}>
      <Hello />
      <button onClick={changeLanguage}>Change Language</button>
    </LanguageContext.Provider>
  )
}

Here’s a working Code Sandbox example. Instead of passing language down to Hello component as prop, you simply grab the context value and use it to conditionally render the output.

Consuming context in class components

In class components, you can get the value of context by using the Context.Consumer component. The component requires a function as its child, because it will pass the current context value into it. You then need to return a React element to be rendered on the screen:

class Hello extends React.Component {
  render() {
    return (
      <LanguageContext.Consumer>
        {(language) => {
          if (language === "fr") {
            return <h1>Bonjour!</h1>;
          }
          return <h1>Hello!</h1>;
        }}
      </LanguageContext.Consumer>
    );
  }
}

I don’t know about you, but the Consumer function pattern looks confusing to me! Fortunately, React team also feel the same way so they created another way to consume context in class components by using the contextType property:

Hello.contextType = LanguageContext

By assigning contextType to the context object, you can use this.context to retrieve the context value. You can also assign the contextType inside the class component as a static field. Here’s the full code:

class Hello extends React.Component {
  static contextType = LanguageContext;
  render() {
    const language = this.context;
    if (language === "fr") {
      return <h1>Bonjour!</h1>;
    }
    return <h1>Hello!</h1>;
  }
}

And here’s another Code Sandbox example for you to inspect.

Separating context from components

Because of the global nature of context API, you will pass its value into many components inside your application. It’s very common to have one provider and many consumers of the Context API as your project grows. As you’ve learned from previous sections, getting the value out of Context API requires you to reference the context object both in function and class components.

You need to organize and separate Context from component to make it more maintainable. Here’s an example project structure that you can follow when you need to use context API.

Take your skills to the next level ⚡️

I'm sending out an occasional email with the latest tutorials on programming, web development, and statistics. Drop your email in the box below and I'll send new stuff straight into your inbox!

No spam. Unsubscribe anytime.