Instructor: 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
In toolbar, we can use the theme context consumer and inside it the user context consumer to render a div.
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.