Learn about the useContext Hook, its use cases and implementation.
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:
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:
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#
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:
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.
Consume the context with useContext#
To grab the context and use whatever value it contains, implement the
useContext Hook like this:
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:
Looks nice and clear and clean, doesn’t it? No more wrapping consumers within consumers to access multiple context objects.
useContextHook 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.
Context also offers a simple alternative to something like Redux. We’re going to study Redux in detail in an upcoming module, but for now just know that, whilst more complex in its implementation, Redux essentially offers the same end result of providing access to a broader idea of state.
It’s also important to note at this point that context is not particularly suitable for two-way communication between components. You can facilitate data back upstream by passing event handlers and functions down via context (you can see an example of this very thing in the official context docs), but its primary purpose is to provide access to global state data across portions of your app without the need for prop drilling. This is generally a one-way street.
Building the useContext example#
Now that we have a better understanding of React’s context and how to use the
useContext Hook, let’s build a simple example that uses context to change themes.
Open up the project we set up in the previous lesson and open the styles file
styles.css. So far, it only has some basic, general styles. We’re going to add a few more for the
UseContextExample component, just to illustrate the theming idea.
Add these styles to the bottom of the file: