Getting to Know Hooks in React: useContext()

R. Cory Stine
4 min readApr 7, 2022

Over the course of the last few weeks, I’ve examined React Hooks and how they help us to make our code simpler and more efficient:

  • useState() allows us to utilize state within a functional component.
  • useEffect() allows us to emulate lifecycle methods within a functional component.

While there are many more useful hooks that can be implemented, there is one prominent option that we have yet to discuss: useContext(). This is another hook that can feel a bit abstract at first, so let’s break down the problem it was designed to solve and then how we apply the solution.

In React, we can pass data from a Parent component to a Child component using props. Prop drilling occurs when props data is passed down through multiple levels, but only truly used in the final level.

Let’s take a look at an example:

import React from "react";
import ChildA from "./ChildA";
const Parent = () => {
const username = "Leonardo";
return <ChildA username={username} />;
};
export default Parent;

Our Parent component sits at the top level. It contains a constant called username that is set to a string of “Leonardo”. It renders a ChildA component and passes down the username data as a prop called username.

import React from "react";
import ChildB from "./ChildB";
const ChildA = (props) => {
return <ChildB username={props.username} />;
};
export default ChildA;

ChildA renders another component, ChildB, into which we pass the username props as username.

import React from "react";
import ChildC from "./ChildC";
const ChildB = (props) => {
return <ChildC username={props.username} />;
};
export default ChildB;

ChildB renders another component, ChildC, into which we again pass the username props as username.

import React from "react";const ChildC = (props) => {
return (
<div>
<p>{props.username}</p>
</div>
);
};
export default ChildC;

Finally, in our ChildC component, we use the username data we received all the way back at the top level, rendering it for the user to see on the page.

Props drilling isn’t inherently a problem — the code still works. However, it creates a lot of repetition and can cause performance issues on a larger scale. Ultimately, the props are being passed down through levels (i.e. ChildA and ChildB) in which they aren’t even being used. It’s unnecessary code and we want to remain DRY (Don’t Repeat Yourself) whenever possible.

This is where useContext() comes into play. React Context allows us to share data — like props and state — between an upper level component and others deeper into the Component Tree without having to resort to prop drilling.

To implement React Context, we need to do three things:

  • Create the context.
  • Provide the context.
  • Consume the context using useContext().

To create our context, all we need to do is import the createContext() function from React and build an instance of it outside of our Parent component function. It should be globally accessible. We can also pass in a default value. For our circumstances, we’ll create a variable called UserContext and set it to an instance of createContext, then pass in a string called ‘Default Value’.

import { createContext } from "react";
export const UserContext = createContext("Default Value");

Next, we need to provide our context to the Child components that wish to access our props data. We do that by wrapping the Children that will be receiving the data within a Provider component. It is mandatory that we include a value within our provider, which will contain the props we want to be passed down.

import { createContext } from "react";
import ChildA from "./ChildA";
export const UserContext = createContext("Default Value");
const Parent = () => {
const username = "Leonardo";
return (
<UserContext.Provider value={username}>
<ChildA />
</UserContext.Provider>
);
};
export default Parent;

Our Parent component looks fairly similar to the way it did earlier. The primary difference is that the ChildA component is wrapped in UserContext.Provider. Now, any children further down the Component Tree will have access to the data set in value. This includes ChildC.

With the context provided, we can move onto the final step: consuming the context. This is where we implement our useContext() hook to pull the data from the Parent into the Child.

import { useContext } from "react";
import { UserContext } from "./Parent";
const ChildC = () => {
const username = useContext(UserContext);

return (
<div>
<p>{username}</p>
</div>
);
};
export default ChildC;

To make sure we have everything we need to connect ChildC to the Parent component, we import useContext from React, as well as UserContext from our Parent file.

We no longer have to pass props into ChildC as an argument. Instead, we call useContext() and pass in the UserContext data, which will return the value that we set in the Provider: “Leonardo”. We set our returned data to a constant called username, which we can be used to in our return statement to render. Here, we display the username as text on the screen.

You’ll notice that I didn’t have to do anything with ChildA or ChildB. We no longer need to pass our props down from the top to the bottom. They simply go directly from the Parent to ChildC where they will actually be used. ChildA and ChildB are no longer middlemen and can do what they need to do without the extra clutter of props.

Before implementing useContext(), you may want to consider a few things.

First, while React Context can save you some lines of code in the connecting components, the Parent and destination Child will become more complex. As such, it’s important to weigh whether or not that added complexity will be balanced out by the space you’ll free up in other components. There’s an argument to be made that this example might benefit from the small amount of prop drilling that it originally utilized. Also, React Context can make testing a challenge, which could slow you down later on in development.

Only you can decide if the trade offs are worth it, but useContext() is a powerful tool for moving props and state data along the Component Tree — and is probably the most effective means of avoiding prop drilling.

Next week, we’ll wrap up our series on React Hooks by digging in to how to create our own!

--

--