Beginner's Guide to React useContext Hook
Learn about the useContext Hook, its use cases and implementation.
Hook: useContext
#
The first Hook we’ll explore in this module is useContext
. Context in React is defined very clearly in the official React documentation:
Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language.
By making various items of data available in a ‘global’ sense, context solves a familiar problem that we saw in the first lesson of the previous module, prop drilling. Prop drilling is the process of passing data down through multiple layers of child components.
It often looks like this:
<App user={user} />
// App then renders...
<Header user={user} />
// Header then renders...
<Navigation user={user} />
// Navigation then renders...
<Avatar user={user} />
// Avatar then renders...
<LoggedInStatus loggedIn={user.loggedIn} />
You can see that the main App
component receives a user
object and then passes it down to the Header
component, which passes it down again to Navigation
, which in turn passes it on to Avatar
, and so on down the tree.
In this really contrived and over-simplified example you can already see how things could get messy if you had a number of different items of data to pass through multiple child components. This practice also creates a lot of redundancy. In essence, a lot of these parent components become middle men for the user
object; they themselves don’t do anything with user
, they merely pass it on down the component tree.
With context, we can create a context provider to wrap our App
component in. This provider would then carry our user data across the components until we hit the Avatar
component. At this point, we’d grab the context using a consumer wrapper.
This non-Hook version of React’s context looks like this:
// Create a context object with a default value of 'null'
const UserContext = React.createContext(null);
function App() {
// grab some user details from somewhere
const userDetails = fetchUserDetails();
// wrap the main child(ren) component(s) in a Context.Provider
return (
<UserContext.Provider value={userDetails}>
<Header />
</UserContext.Provider>
)
}
// Header and other components omitted...
function Avatar() {
return (
<UserContext.Consumer>
{value => <p>{value.name}</p>}
</UserContext.Consumer>
);
}
Straightforward enough, but it looks a little clunky. Things only get worse when you start to use multiple slices of context; you have to wrap providers inside providers and do the same with the consumer end.
Using the useContext Hook#
The useContext
Hook offers a much more efficient experience when detailing with context. Much of the way you define a context object will be the same - this holds true for the provider wrapper too. The main difference is how you consume the context; that’s where useContext
comes in.
Let’s walk through the steps needed to create and consume context in React.
Define a context object#
The first step is to define your context object:
// i.e. MyContext.js
export const MyContext = React.createContext('default value here');
The default value for your context can be anything you like: a primitive value, function, object, you name it.
Wrap the top-level component in a context provider#
Next, you’ll want to wrap whatever your topmost component happens to be in a context provider. The topmost component might be your app’s entry point, or it might be a specific area within your app that contains a specific group of components, such as an account management area.
// i.e. MyTopLevelComponent.jsx
import { MyContext } from './MyContext.js';
function MyTopLevelComponent() {
return (
<MyContext.Provider value={'specific value here'}>
<ChildComponent1 />
<ChildComponent2 />
<ChildComponent3 />
</MyContext.Provider>
);
}
Consume the context with useContext#
To grab the context and use whatever value it contains, implement the useContext
Hook like this:
// i.e. ChildComponent1.jsx
import React, { useContext } from 'react';
import { MyContext } from './MyContext.js';
function ChildComponent1() {
const myContextValue = useContext(MyContext);
return (
<p>{myContextValue}</p>
);
}
Using multiple contexts#
If for whatever reason you need to access multiple contexts within a component, the useContext
Hook makes it really easy. You simply call useContext
as many times as you need, one for each context object you want to consume:
function MySuperComponent() {
const user = useContext(UserContext);
const theme = useContext(ThemeContext);
const messages = useContext(MessageContext);
return (
<div style={{background: theme.background}}>
<h1>Welcome {user.name}</h1>
<p>{messages.greetingMessage}</p>
</div>
);
}
Looks nice and clear and clean, doesn’t it? No more wrapping consumers within consumers to access multiple context objects.
Note, the
useContext
Hook is awesome, but it still requires the use of a provider further up the tree to provide the context that it consumes!
When to use Context#
Context is a great fit for some use cases, theming being one of them. Its primary purpose is to provide a means to share data between many components within a nesting structure. However, its use can be abused and you may be tempted to use context when some component refactoring (or component composition changes) could achieve the same result.
Using context can also make components less reusable as they’re more inherently tied to their context-providing parents, higher up the component chain.
The official React documentation on context has a great example on using component composition (i.e. how you create your components) over using context, and I’d recommend checking those out.
This page is a preview of Beginner's Guide to Real World React