Implement the Provider Pattern with React Context

Kent C. Dodds
InstructorKent C. Dodds

Share this video with your friends

Send Tweet
Published 4 years ago
Updated a year ago

The prop drilling problem can be frustrating for data that changes over time and is needed throughout the application. Luckily for us, we can use React's createContext API to make state (like on) and state updater functions (like toggle) available anywhere in the tree. In this lesson let's see how to side-step the prop drilling problem with a custom context provider and consumer.

Instructor: [00:00] Here, we have a simple UI with a switch and a text that updates as we switch. The way that we're accomplishing this is we have a toggle component that manages the state and renders with a render prop API. It forwards on the state and a mechanism for updating that state to the children.

[00:18] Then, in our usage of the toggle component, our children function takes the on and the toggle and doesn't actually need it itself, so it forwards it on to layer one. Layer one actually doesn't need the on or toggle props either. It forwards those onto layer two.

[00:35] Layer two is using the on prop to render our text and then forwards the props on to layer three. Layer three doesn't need those, so it forwards it on to layer four. Layer four needs both of them for the switch component.

[00:47] What we're looking at here is what's called the prop-drilling problem. We have to drill props down through components even when some of those components don't actually need those props themselves.

[00:57] The problem that this presents is let's say that the switch component is refactored and changed. It doesn't need the on prop anymore, so we remove that. Now we have to update ourselves to no longer forward the on prop to layer four because we're passing more props than are necessary. We could actually remove it from layer three as well.

[01:15] Let's say the inverse happens. Here we say on is on. Now we actually do need to have the on prop. We have to remember, "Layer four needs to forward it, and layer three needs to forward it."

[01:28] This example is clearly contrived, but if you've been using React for a while, you've definitely felt this pain before. This is what the provider pattern allows us to avoid. Luckily for us, React exposes an awesome API to solve this problem. It's called context.

[01:42] Let's go ahead and refactor this so that we don't have to forward props everywhere. The first thing we'll do is we'll create a ToggleContext from React.createContext. We'll give it a default of on is false and toggle is an empty arrow function. Then we'll update the render method of the toggle component so that it uses the ToggleContext.Provider.

[02:03] Here we'll return ToggleContext.Provider. The value will be this.state. We'll forward along the props. Then we can get rid of the old render prop API and only expose this provider API. The consumers of this API are going to need the toggle function, so we'll move state down here below the toggle and add toggle is this.toggle on our state.

[02:27] Next, I'm going to add static Consumer = ToggleContext.Consumer. Doing this allows me to not have to expose the ToggleContext itself but simply expose the consumer on the toggle. This isn't absolutely necessary for the context API, but it's something that I like to do to associate the consumer with the toggle component.

[02:49] Let's go ahead and update our usage. Because we're not using on and toggle here, and we're not even using the render prop API, we're going to go ahead and render layer one with no props. For layer one, it's not accepting those props anymore. It doesn't need to forward those on to layer two.

[03:06] Now layer two isn't accepting those props, but it does need the on state. That's where we're going to use Toggle.Consumer. This is a render prop API itself. It's going to get our context value, which in our case is an object that has on and toggle. We'll destructure on. We'll put our fragment directly in here.

[03:26] With that, we don't actually need to forward anything on to layer three. It's not using those props either. We'll get rid of that. Layer three will no longer accept these as props. We don't need to forward that on to layer four.

[03:38] Layer four will need to get those props from somewhere, but it's not going to get it from layer three. It's going to get it from Toggle.Consumer. It's going to get our value, which is on and toggle. We'll destructure both of those, on and toggle. Then, we'll close off our Toogle.Consumer. With that, everything is functioning exactly as it was before.

[04:01] Now it's easier to make these updates. If the switch no longer needs on, then we can just remove it. We no longer need to accept it, but we don't have to update anything else in the tree for that to work. In the future, if it actually does need on, we pull that out of context, and we can specify that.

[04:16] In review, the provider pattern is built into React with the React.createContext API. With it, we can provide a default value for if we wanted to render one of these outside of the context of a ToggleContext.Provider. We render the ToggleContext.Provider with our value that only changes when the state changes. Then we forward on any props.

[04:37] Doing things this way, actually, it makes it easier to test the toggle component, because we can pass our prop to override the value if we need to. Testing is made a little easier.

[04:46] We also added a consumer static property to our toggle to make it easier to associate the consumer with the toggle component itself. We don't have to expose the ToggleContext directly. We refactored everything. We no longer need the render prop API from toggle because we don't need to get that state here.

[05:03] We render layer one, which simply renders layer two. Layer two needs to reach inside context, and so it uses the Toggle.Consumer. Then it renders layer three, which will render layer four. Layer four will render the Toggle.Consumer because it needs to reach into context to get on and toggle.

Jaime Sangcap
Jaime Sangcap
~ 3 years ago

What is the purpose of spreading the props in <Toggle.Provider value={{//value here }} {...this.props} /> ? Is it to provide a way to override the provider value?

Kent C. Dodds
Kent C. Doddsinstructor
~ 3 years ago

Is it to provide a way to override the provider value?

That's exactly the reason. It's useful primarily for testing.

Jaime Sangcap
Jaime Sangcap
~ 3 years ago

Got it! thanks Kent, great course. I hope to see more courses from you :smile:

Steve Gibbings
Steve Gibbings
~ 3 years ago

Kent, I’m interested in comparing the view from the React docs on the Context API which states “such as the current authenticated user, theme, or preferred language” and “Apply it sparingly because it makes component reuse more difficult.”. The use in your example feels like it is not in the situations from the official docs, though they clearly don’t say don’t do it.

What’s your feeling on what the docs say about context and comparing how you have used it for local, often changing state, and methods.

I’m not experienced enough to feel I have my own firm view on this. If I can understand your “justification” of the use and how you feel about what the docs say it’s for I think that would help me greatly.

Steve Gibbings
Steve Gibbings
~ 3 years ago

I’d add, “such as the current authenticated user, theme, or preferred language” to me implies relatively infrequently changed and not updated by lower components.

Kent C. Dodds
Kent C. Doddsinstructor
~ 3 years ago

Hi Steve,

I think they say that to deter people who are just getting started with React. Once you have a good understanding of the drawbacks, then you know where to appropriately apply context. Generally I'd say avoid it if you can and instead colocate your state :)

Steve Gibbings
Steve Gibbings
~ 3 years ago

Thanks Kent,

I’ve read those posts ( I think all of yours as I’m a fanboy!)

I think I’m not clear on when to just use local state ( other than for the obvious controlled components) vs context API ( I’ve only used useState ) vs a 3rd party state management package like the ubiquitous redux).

I can see and agree with not going for an abstraction that isn’t necessary (yet) it’s reading about comparisons between those with indications of when a more complex abstraction is useful and why. Other than trivial examples I am not clear when I should go for context or a further abstraction.

I’ve followed your advanced react components on egghead and on your tube. Is that the same content as your remote/face-to-face workshops?

If you could point me further than would greatly help me.

Sent from my iPhone

Kent C. Dodds
Kent C. Doddsinstructor
~ 3 years ago

My remote workshop material has all been updated with React hooks. Some of the patterns are no longer necessary when you can use hooks. I do plan to eventually record videos of that material.

Steve Gibbings
Steve Gibbings
~ 3 years ago

Thanks Kent

Sent from my iPhone