Creating Providers and Consumers with the React Context API

The React documentation has been warning us for a long time now that context shouldn't be used and that the API is unstable. Well, with the release of React 16.3, we're finally getting a stable context API and what's even better is that it has received a makeover and the dev experience is fantastic! In this lesson, we'll look at an example app that passes props down several levels deep into the component tree and replace all that prop drilling with the new context API. We'll see how to create a Provider and a Consumer, how to use them in the code and we'll take a look at how the default properties that get passed into createContext come into play.

note: we're using an alpha release of 16.3 for this lesson, but the official release should be soon

You must be a Member to view code

Instructor: 00:00 Here, we have a simple app that was a user profile from a service, places that on the state of the top-level app component, and then that profile object is passed down as props.

00:09 We'll see that our profile is being passed in as the profile prop to this page wrapper component. If I open that up, we receive that. Page wrapper gives us a header and a footer, and it uses that profile wrapper, where it's spreading that profile prop and passing those individual properties down as props.

00:27 If I look at profile wrapper, we'll see that props are once again passed down into profile details. Profile wrapper provides this heading, and then passes those properties down. Profile detail finally uses those properties and displays them.

00:42 In this simplified scenario, it would make sense to just move our API call down to the profile detail level and keep track of the profile there, as state. But, for the purpose of this lesson, let's imagine that we need that profile at the top of our tree for use in other children of the app component.

00:58 Let's look at how we can use the new React Context API to keep our state in the app component, but avoid the need to pass these props through every level of our component tree.

01:08 We'll start by creating a new file. I'm going to add this to the source directory, and I'm going to call this profileContext.js. In profileContext, I'm going to start by importing the createContext function from React. Let's define a new constant called profileContext. This is going to equal a call to createContext, and we're going to pass this a default value.

01:36 For now, we're just going to use an empty object, but we'll look at that again in a moment. ProfileContext is going to have two properties on it. Each of those properties is a component. One is called provider. The other is called consumer.

01:48 We're going to export those individually as profileProvider and profileConsumer. We'll start with export const, profileProvider, and that's going to equal a reference to our profileContext, grabbing its provider property. Then we'll do the same thing for consumer.

02:10 I'll export const profileConsumer, and that'll be profileContext.consumer. With that in place, let's save this file. I'll get this out of the way. I'll go back into index.js, and I'm going to import profileProvider, and I'm going to get that from our new profileContext file.

02:43 I'll scroll down, and we're going to update the render method. I'm going to return page wrapper, but I'm going to wrap it in our profile provider. I'll throw some parens in there. Inside our return we're going to use profileProvider, and that's going to get a value prop. That prop is going to be this.state.profile.

03:08 I'm going to take this out of here, and move it up. We can close that, and then we can take our page wrapper, get rid of that prop, and we can just move it up. We'll save that. Now, if we refresh our preview, our first name and last name are now blank, because our profile detail component is looking for props that aren't being passed down anymore.

03:37 Let's open pagewrapper.js. Since those props aren't being passed in, we don't need to spread them and pass them here. We'll take that out of there, and save that. We'll do the same thing with profile wrapper. We're getting props, but we're not using them, so we'll take that out of there.

03:56 We'll go into profile detail. Here, we're looking for props called firstName and lastName, and we're not getting them, so now we need to get them from that context that we're passing into that provider. At the top of our file, we're going to add a new import. We're going to import profileConsumer from our profileContext file.

04:22 Let's drop down into our component. We're going to use our profileConsumer as a component. I'm going to drop down and I'm going to throw in a close. The way profileConsumer works is it take a function as its child. We're going to use curly braces here, and we're going to pass this a function.

04:41 This function is going to get executed by the profileConsumer, and it's going to get Context as an argument. I can just supply Context here, throw in an arrow function. My arrow function is going to return the rendered output, using those Context values.

05:00 I'm going to take everything that I had originally in here, and I'm going to move that up into the return for this child function. I'm going to update these. Instead of using props.firstName, I'm going to use context.firstName and context.lastName.

05:21 With that done, I'm going to save this profile details component. I'm going to refresh the preview. We'll see that our first name and last name are showing up without all that prop drilling to get our props from the top level down to the bottom. Context is allowing us to bypass those levels and just get access to that data where we need it.

05:42 Let's take another look at where we created that context. Our profileContext file calls createContext. It's passing in this empty object, and that gives us back our provider and our consumer. This empty object is actually a set of default values. Let's see how that works.

05:59 I'm going to come in here and I'm going to give this a first name property, and we'll do something other than what we have currently. We'll do Sally, and we'll do a last name of Anderson. I'll save that. Then, let's go to index.js, and remove this value from profileProvider.

06:18 I'll take that off of there and I'll save it. If I refresh the preview, we're going to get an error. The default values don't replace the need to pass a value into our profile provider, so let's put that back, make sure everything still works.

06:36 We're back to where we started. We have this default value, and if we don't pass value into profile provider, that doesn't help up out. Where it does come in handy is if we use our consumer anywhere in our tree that doesn't have a provider above it.

06:56 In this case, we'll just return page wrapper without that provider. We'll save it, and I'll refresh the preview again. This time, we'll see we're getting our default values. The default values are used if you don't have a provider above your consumer. If you do have provider, though, you need to provide that value or things could potentially break.