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.