A Step-by-Step Guide to Using React's useContext Hook

Context is a powerful API to pass state within React applications, and with the introduction of the useContext Hook, accessing state in any component (class or functional) is possible.

This lesson preview is part of the The newline Guide to Modernizing an Enterprise React App course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to The newline Guide to Modernizing an Enterprise React App, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course The newline Guide to Modernizing an Enterprise React App
  • [00:00 - 00:17] The use context hook simplifies using the context API within a React application, even in functional components. For the longest time, I understood why context existed, to avoid passing props through React components that didn't need them to reach the components that did.

    [00:18 - 00:35] But what I couldn't wrap my head around was how to not only consume the already defined state of context at the child component level, but also to update that same state in the parent component from the child. When I finally figured that out, things clicked into place for me.

    [00:36 - 00:51] And the first time I really started to take advantage of context was with the release of the use context hook. In this lesson, we'll learn how the use context hook can be employed to simplify passing state around within a React application.

    [00:52 - 01:03] My code example for this lesson is a multi-file example to illustrate how use context could work in a real world scenario. In it, a context is created to capture state in a parent component.

    [01:04 - 01:16] An intermediate component that does not need that parent state contains a functional child component that does rely on the parent state and also needs to update it. Use context helps make all of this possible.

    [01:17 - 01:24] So here is our code sandbox example. There is a component that's in between what we call the app file and the drawer .

    [01:25 - 01:36] In the intermediary component, which I have named in between component, it doesn't need any data from the parent container. However, the drawer, which is the child of the in between component, does.

    [01:37 - 01:53] So if we click this button, we see that the drawer quote unquote opens and turns the background pink. And once we close it again, it closes the drawer, turns the background back to the plain white color.

    [01:54 - 02:06] So use context allows functional child components to consume and update parent state. In the example, after the app initially loads, when you click open drawer from parent button, you see the purple background pop up.

    [02:07 - 02:19] That's the drawer with a new button that is titled closed drawer from child. If you've used context before, you may be pretty familiar with how this operates, but let's briefly discuss how use context works as a hook.

    [02:20 - 02:27] And then we'll go through the various pieces of the code and connect the dots. So how does use context actually work?

    [02:28 - 02:47] The use context hook accepts a context object, which is the value returned from calling react.create context, which you would use to create a context regardless of if it was a functional or a class based component. And that returns the current context value for that context.

    [02:48 - 03:04] The current context value is determined by the value prop of the nearest sample context provider above the calling component in the tree. So basically, whatever context is wrapping the place where that context is actually being used.

    [03:05 - 03:18] So when the nearest provider above the component updates, this hook will trigger a rerender with the latest context value passed to that sample context provider. If the above paragraphs sounded like a lot of jargon to you, I get it.

    [03:19 - 03:27] I said the word context a lot. The next section explains what I'm describing step by step with code examples though, so bear with me.

    [03:28 - 03:41] The argument passed to the use context hook must be the context object itself. And just like with all the other React hooks, you can use multiple different contexts within the same functional component, which is very handy sometimes.

    [03:42 - 03:53] So here is an example that I wrote out. We have a context that we named sample context, a second one that we named test context, and a third one that we named another context.

    [03:54 - 04:04] All three of these can be used by one child component. Any component calling the same use context data source will always rerender when the provider's context value changes.

    [04:05 - 04:17] This is something to keep in mind. So what this means is if you've got two components that are subscribed to the same provider and one component updates the provider's value, both of those components will re render.

    [04:18 - 04:39] You don't need to worry too much about this now, but it is good for you to be aware because rerendering the component can become expensive and you can optimize it using React's use memo hook, which I've linked to if you want further documentation, but that is beyond the scope of this lesson. And frankly, it only needs to be undertaken once performance actually becomes a problem.

    [04:40 - 04:50] If you try to optimize it ahead of time, it will just lead to more headaches and it is probably unnecessary. So let's get into the use context code example now.

    [04:51 - 05:00] So first we're going to check out the drawer context file, which I will open up over here in our code sandbox. The first file to look at is the drawer context.

    [05:01 - 05:27] As stated above, context object is created by invoking react dot create context right here and then wrapping that new context around the parent component providing the values to that context. So inside of drawer context, all we're doing is calling create context and then adding a couple of values, one called show drawer and one called toggle drawer state.

    [05:28 - 05:35] This is a constant and this is a function. If you've used context before, the code snippet shouldn't look too foreign.

    [05:36 - 05:50] It's a simple new context object named drawer context with a show drawer state value and a toggle drawer function. Both are undefined or empty properties that will be supplied with values in the app component where the context provider will be initialized.

    [05:51 - 06:04] I like to define my properties in my context objects. This is my own personal preference, but I like to explicitly define the properties the context will be responsible for, although I don't usually give them default values.

    [06:05 - 06:11] I do this so that I can quickly glance back at a specific context file and see what it contains or what it should contain. It's not a necessity though.

    [06:12 - 06:26] If you would prefer to just leave them completely blank and fill in those values when you're in the actual context provider component, you can do that too. Next we're going to open up the app dot JS file.

    [06:27 - 06:36] So the app dot JS file is where the drawer context component we just looked at is initialized as a context provider. Here I am importing it right here.

    [06:37 - 06:54] With this component, I can define a local state variable named show drawer right here and a function named toggle drawer state. So in this example, the local variables possess the same name as the properties that I set in the drawer context component, but it doesn't have to be that way.

    [06:55 - 07:14] I do it for convenience, ease of understanding how these variables relate to one another, and so that I can take advantage of the JavaScript ES6 property shorthand. If I wanted to name my local variable something different like is drawer open, I would assign it as the value to associate with show drawer in the context provider like so.

    [07:15 - 07:35] We would just say value show drawer, and then with the colon is drawer open to connect it to this local variable name. So once those two variables are defined, they're passed into the drawer context dot provider right here using our value equals show drawer and toggle drawer state.

    [07:36 - 07:58] And this drawer context provider wraps our entire JSX function, which you can see below right here drawer context provider and then the closing HTML element. So you'll notice that in the JSX itself, the variables defined in this component are referenced as well.

    [07:59 - 08:11] The show drawer state is used to show or hide the button to open the drawer and the buttons on click function calls the toggle drawer state function. The flip show drawer's value from false to true in the DOM.

    [08:12 - 08:29] Although this toggle drawer state function is simple, by defining a function that can be assigned to a property in the context object, we can easily handle more complicated data transformations before updating the state when needed. So I really want you to pay attention to toggle drawer state.

    [08:30 - 08:54] That function is the key to updating the state of a parent variable from a child component. Although react props are immutable once passed to child components, which is why you can't just pass set show drawer in the context or directly to a child component through props, it will not update the state of show drawer in the parent declaring a function that can take in a variable and update state within the parent component does work.

    [08:55 - 09:12] This was a missing link that I had trouble recognizing as I learned how to use context myself in case it helps make it click for you to. So once the button is clicked, it disappears from view and the immediate child component called in between component and its own child component can take center stage.

    [09:13 - 09:17] We will open those up. This in between component is just here for the example.

    [09:18 - 09:34] Very briefly we'll look at the aptly named in between component. This functional component exists for the sole purpose of being a component in between the parent controlling the state and the child consuming it to better illustrate why you'd want to avoid proctoraling the values from app.js.

    [09:35 - 09:44] So if we look at this, it is purely a pass through. And what we need to focus on is drawer component, which is the child that is actually consuming that state.

    [09:45 - 09:51] So as you can see, this component needs nothing from its direct parent app. It is as simple as functional components get.

    [09:52 - 09:56] It is purely doing display. Its child drawer component does.

    [09:57 - 10:07] But since in between component itself needs none of these values, there's no point passing them through this component as props to reach the drawer component if we can avoid it. Finally, we will look at the drawer component.

    [10:08 - 10:15] The child who needs use context in order to work. Okay, we have reached the child component where the drawer lives.

    [10:16 - 10:24] This is finally where our use context hook comes into play. The use context hook is imported at the top of the file along with the drawer context.

    [10:25 - 10:48] Then right inside the component initialization, a local variable named drawer context right here is created by passing the drawer context object into our use context hook. So now this local drawer context has access to all the state variables passed into the provider in our parent component, which are show drawer and toggle drawer state .

    [10:49 - 10:58] In drawer components JSX, a local button is present. This button's on click function is attached to the drawer context dot toggle drawer state function.

    [10:59 - 11:08] And it passes the false Boolean in, which sets the show drawer state all the way back up the react hierarchy tree to false. And that's all there is to it.

    [11:09 - 11:32] Once the button is clicked, the drawer that's purple background disappears once again, and we're left viewing the open drawer from parent button. My aha moment for this was understanding that while I couldn't directly pass a state's setter like show drawer to a child component and update that state in the parent, I could pass a function to trigger that state to update in the parent component.

    [11:33 - 11:44] Once I figured this out, I really started to see the value of context. To me, the hooks syntax of calling use context is also a lot less confusing than trying to wrap components in context dot consumer.

    [11:45 - 12:02] The provider became more straightforward as I used it, and because only one component can be responsible for the state variables going into the context. I always felt a little shaky on the consumer pattern with class components though, since multiple children can consume the context from the provider.

    [12:03 - 12:11] Let me dispel a common misconception that comes with the react context API. Context is not the same thing as Redux.

    [12:12 - 12:24] It is not a one-to-one replacement for the state management library Redux or recoil. When you think about what context actually does, it functions to pass state around an application without having to proctoral.

    [12:25 - 12:37] So it gets state to a component several levels deep in a component tree without the intermediate components being aware of that state. The state itself is still defined within a particular component.

    [12:38 - 12:51] But Redux or recoil does is own and manage a centralized state throughout an application so that that state is available to all components that need it. But none of the components are responsible for defining that state first.

    [12:52 - 13:03] This is beside the point, but I think that it's worth stating because there are so many conversations about replacing Redux with context and it's not so simple as that . The two don't really do the same thing.

    [13:04 - 13:15] And yes, many projects are probably over-engineered, employing Redux before they really need to, when a few context components would suffice just fine. And with that, we've made it to the end of another lesson.

    [13:16 - 13:25] Well done. Okay, there's one final hook that I would like to cover and it's arguably the biggest and most important update that the React team introduced with hooks, the custom hook.

    [13:26 - 13:28] Yeah, you heard that right. Custom hooks.

    [13:29 - 13:29] Ready?