React Context API is not a state management tool

Notes on React Context API, how to create a Context, consume it, and pass values to a Context Provider.

This may sound obvious, but React Context API is not a state management tool per-se.

What I mean is that the Context alone is not enough to manage application's state without a companion hook like useState or useReducer.

Creating a Context

To create a new Context in React we call createContext():

const MyContext = React.createContext();

Once a Context is in place we can provide an initial value to the Context Provider on the value prop:

import React from "react";

const MyContext = React.createContext();

function App() {

  const userName = "Juliana";

  return (
    <MyContext.Provider value={userName}>
    {/* Some children */}
    </MyContext.Provider>
  );
}

However, this value is static, far from being a state management solution.

Providing state to the Context

To make things dynamic, we can use useState or useReducer as a source for the Context value:

import React, { useState } from "react";

const MyContext = React.createContext();

function App() {
  const [userName, setUserName] = useState("Juliana");

  // use setUserName() somewhere in the app to update the state  

  return (
    <MyContext.Provider value={userName}>
    {/* Some children */}
    </MyContext.Provider>
  );
}

Reading the Context

To access the Context value from any child component lower in the tree, we wrap said tree in a Provider:

import React, { useState } from "react";

const MyContext = React.createContext();

function App() {
  const [userName, setUserName] = useState("Juliana");

  // use setUserName() somewhere in the app to update the state

  return (
    <MyContext.Provider value={userName}>
      <Header />
      <Footer />
    </MyContext.Provider>
  );
}

Then, to read the value from any child component we can either use Context Consumer with render props, or better, the useContext hook:

function Header() {

  const userName = useContext(MyContext);

  return (
    <header>
      <h1>Hello {userName}</h1>
    </header>
  );
}

function Footer() {

  const userName = useContext(MyContext);

  return (
    <footer>
      <p>Hello {userName}</p>
    </footer>
  );
}

Caveat: components connected to the Context with useContext don't bail out by default from re-rendering. See also useContext doc.

How about Redux vs Context API?

The false Redux vs Context API dichotomy is used as an argument to demonstrate that Context API completely supersedes Redux.

This can't be farther from the truth. Alongside with the performance optimizations Redux offers out of the box, the Context API don't have:

  • middleware.
  • time travel.
  • state persistence.

All these feature must be implemented from scratch, with hooks. While this might sound feasible, reinventing the wheel is not always the best thing to do.

Valentino Gagliardi

Hi! I’m Valentino! Educator and consultant, I help people learning to code with on-site and remote workshops. Looking for JavaScript and Python training? Let’s get in touch!

More from the blog: