React Context provides a way to manage state globally across a component tree. It can be used in conjunction with the useState
Hook to share state between deeply nested components more efficiently than using useState
alone.
The Challenge
State should be managed by the highest parent component in the component hierarchy that needs access to the state.
To illustrate, consider a scenario where we have several nested components. The topmost component and the bottommost component both need access to the state.
Without Context, we would have to pass the state as “props” through every nested component, a process known as “prop drilling”.
Example
Passing “props” through nested components:
import { useState } from "react"; import ReactDOM from "react-dom/client"; function MainComponent() { const [user, setUser] = useState("Alex Johnson"); return ( <> <h1>{`Hello ${user}!`}</h1> <IntermediateComponent1 user={user} /> </> ); } function IntermediateComponent1({ user }) { return ( <> <h1>Intermediate Component 1</h1> <IntermediateComponent2 user={user} /> </> ); } function IntermediateComponent2({ user }) { return ( <> <h1>Intermediate Component 2</h1> <IntermediateComponent3 user={user} /> </> ); } function IntermediateComponent3({ user }) { return ( <> <h1>Intermediate Component 3</h1> <FinalComponent user={user} /> </> ); } function FinalComponent({ user }) { return ( <> <h1>Final Component</h1> <h2>{`Hello ${user} again!`}</h2> </> ); } const root = ReactDOM.createRoot(document.getElementById('root')); root.render(<MainComponent />);
In this example, even though components 1-3 don’t need the state, they still have to pass it along to reach the final component.
The Solution
The solution is to create a Context.
Creating Context
To create a Context, import createContext
and initialize it:
import { useState, createContext } from "react"; import ReactDOM from "react-dom/client"; const UserContext = createContext();
Next, use the Context Provider to wrap the components that need access to the state.
Context Provider
Wrap child components with the Context Provider and supply the state value:
function MainComponent() { const [user, setUser] = useState("Alex Johnson"); return ( <UserContext.Provider value={user}> <h1>{`Hello ${user}!`}</h1> <IntermediateComponent1 /> </UserContext.Provider> ); }
Now, all components within this tree will have access to the user Context.
Using the useContext
Hook
To access the Context in a child component, use the useContext
Hook.
First, include useContext
in your import statement:
import { useState, createContext, useContext } from "react";
Then access the user Context in any component:
function FinalComponent() { const user = useContext(UserContext); return ( <> <h1>Final Component</h1> <h2>{`Hello ${user} again!`}</h2> </> ); }
Full Example
Example
A complete example using React Context:
import { useState, createContext, useContext } from "react"; import ReactDOM from "react-dom/client"; const UserContext = createContext(); function MainComponent() { const [user, setUser] = useState("Alex Johnson"); return ( <UserContext.Provider value={user}> <h1>{`Hello ${user}!`}</h1> <IntermediateComponent1 /> </UserContext.Provider> ); } function IntermediateComponent1() { return ( <> <h1>Intermediate Component 1</h1> <IntermediateComponent2 /> </> ); } function IntermediateComponent2() { return ( <> <h1>Intermediate Component 2</h1> <IntermediateComponent3 /> </> ); } function IntermediateComponent3() { return ( <> <h1>Intermediate Component 3</h1> <FinalComponent /> </> ); } function FinalComponent() { const user = useContext(UserContext); return ( <> <h1>Final Component</h1> <h2>{`Hello ${user} again!`}</h2> </> ); } const root = ReactDOM.createRoot(document.getElementById('root')); root.render(<MainComponent />);