Pass Data through a Component Tree using Context Providers and Consumers in React 16.3

Nik Graf
InstructorNik Graf

Share this video with your friends

Send Tweet
Published 4 years ago
Updated 3 years ago

With React 16.3 a new Context API was introduced. It provides a way to share values like that are considered global between components without having to explicitly pass a prop through every level of the tree.

When creating a context we get a Provider to pass values into the tree, as well as Consumer in order get them out.

Instructor: [00:00] Here, we have an application rendering two buttons inside a toolbar. The button component accepts a theme property, which can be either dark or light. Choose the same theme and the whole application will manually pass the theme prop for toolbar in order to style the button components. This can become painful if every single button in the app needs to know the theme because it would have to be passed through all components.

[00:25] Context can help here since it's designed to share data that can be considered global for a whole tree of React components. Examples would be the currently authenticated user, the theme, or a preferred language. In order to create the context, we call React.createContext. It will return an object containing the provider and the consumer. CreateContext accepts one parameter, the default value. In our case, we're going to set it to dark.

[00:56] Instead of destructuring, we're going to assign it to theme context and export it. We now wrap our toolbar in the theme context provider and provide the value light. The value is passed down to the component tree below and any component can read it, no matter how deep it is.

[01:18] Inside button, we can use a theme context consumer. A consumer requires a function as a child. The function receives current context value and returns a React node. For our theme parameter, React will find the closest theme provider in the tree above and use its value.

[01:49] In toolbar, we can remove the theme props. Success. As you can see, the buttons are now themed light using the context API. By the way, if you remove the theme context provider in the App component, deprecation will still work properly.

[02:07] The consumers simply will fall back to the default value we provided when creating the context. This is especially helpful for testing components in isolation without wrapping them. We can also remove the prop theme on toolbar since we don't use it anymore.

[02:24] What's next? Of course, a value can be more complex. For example, instead of just passing down a string value, we could pass down a more complex data structure like an object.

[02:43] In App, we also have to update the value. We import themes and provide the dark theme to the provider. In the button, we leveraged a theme object to update our button style. So far so good. Sometimes, it is necessary to update the context from a component that is nested somewhere deeply in the component tree.

[03:07] In this case, we can pass a function down through the context as part of the object to allow the consumer to update the context. We'll replace the theme with an object containing the properties theme and toggle theme. In App, we add state, keeping track of the current theme, as well as a theme target function for updating state.

[03:36] Both are passed to the theme context provider as part of the state object. Next up, we updated our button component. We structured a consumer function parameter and extract the theme. We add another theme context consumer to the toolbar, destructure the parameter to get the toggle theme, and add a button that toggles the theme once clicked.

[04:03] Let's give it a try. Works fine. There's one gotcha though I want to make you aware of. When passing a value to provider, avoid doing this. It can lead to unintentional renders in consumers since every time the provider is rendered, a new object for the context value is created. The root cause here is that context uses reference identity to determine when to rerender all the consumers. Let's undo our change and move on.

[04:35] For clarification, consumers and providers from different context instances are independent of each other. In order to use multiple providers and consumers, you need to nest them. Let's see an example. Here, we create a user context containing a name. In App, we can import the user context and pass the user to the user provider.

[05:09] In toolbar, we can use the theme context consumer and inside it the user context consumer to render a div.

[05:30] Voila. Now, we can also switch to theme for the div while the user is provided through the user context. I hope this lesson was a good introduction to the context API. Keep in mind in real world cases don't use context just to avoid passing down props a few levels. Stick to cases where the same data needs to be accessed in many components at many levels.

Bogdan Soare
Bogdan Soare
~ 4 years ago

The videos are well explained but please make some original examples instead of just using the ones from the docs

Nik Graf
Nik Grafinstructor
~ 4 years ago

Thanks for you feedback!

My goal was to find the best possible way to explain the concept. For the other lesson I found other more useful, but particularly for this one the docs examples were the best I could imagine. Why is it a concern to use them?

John Darryl Pelingo
John Darryl Pelingo
~ 4 years ago

As someone who has read the docs and then watches these very informative videos, I have had a lot of lightbulbs turned on. Thank you and keep it up!

Nik Graf
Nik Grafinstructor
~ 4 years ago

Really glad to hear John, thanks 🙌

Nick
Nick
~ 4 years ago

I had same impression as Bogdanich Soare:

The videos are well explained but please make some original examples instead of just using the ones from the docs

Perhaps you should offer more than a straight copy of the official docs, even if you believe they are the best example you can find. You ask why. The answer is that it’s a waste of members’ time to watch a video presentation of exactly the same text-based material they’ve already consumed online -- especially when that text-based material is more extensive.

dong cai
dong cai
~ 4 years ago

Thanks for your videos Nik. I got one question.

removing the theme context provider in the App component, deprecation will still work properly. The consumers simply will fall back to the default value we provided when creating the context. This is especially helpful for testing components in isolation without wrapping them.

What does that mean? Does it mean the the context value falls back to the default value "dark". It would be more useful if you could expand it and talk about how to test Context component.

By the way

There's one gotcha though I want to make you aware of. When passing a value to Provider, avoid doing this. It can lead to unintentional renders in Consumers since every time the provider is rendered, a new object for the context value is created. The root cause here is that context uses reference identity to determine when to re-render all the Consumers.

This is the exact gotcha moment I came across. Thank you for your tip.

Nik Graf
Nik Grafinstructor
~ 4 years ago

@dong Yes, so if you have component which has a consumer, but in a test case you only want to render this component and not wrap it in a provider from that context e.g. ThemeContext it will use that default value. Does that help?

Ilham Wahabi
Ilham Wahabi
~ 4 years ago

Thank you for the lesson, but your voice is a bit hard to hear (too calm though). Its even maximum volume in my computer and still got lost in some cases.

Nik Graf
Nik Grafinstructor
~ 4 years ago

@Ilham thx for the feedback, will try to improve the recording next time