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.