Managing state is arguably one of the hardest part of any application and, It's why there are so many state management libraries available. Search NPM and you will find a ton of them just based on Redux.
React Context is one of the core state management features and when your application grows in size you should consider giving it a try. Many third-party libraries like Redux are using it under the hood anyway, so why not learning about it.
If your application component hierarchy grows in vertical size, it becomes tedious passing props several React components down -- from a parent component to a deeply nested child component and, most often all the components in between are not interested in these props and just pass the props to the next child component until it reaches the desired child component.
In React this is called "prop drilling" and, I personally do not like it at all since I always look after clean solid components with few dependencies. Below you can see the hierarchy when prop drilling happens down the tree:
+--------------+
| A |
| |Props |
| v |
+------+-------+
|
+-------+---------+
| |
| |
+--------+-----+ +------+-------+
| | | + |
| B | | |Props |
| | | v |
+--------------+ +------+-------+
|
+------+-------+
| + |
| |Props |
| v |
+------+-------+
|
+------+-------+
| + |
| |Props |
| C |
+--------------+
This clutters every component in between which has to pass down these props without using them but React Context gives you a way out of this mess - instead of passing down the props down through each component, you can tunnel props through these components implicitly with React Context. React context can be viewed as a message channel for components. If a component needs access to the information from the context - it can consume it on demand, because a top-level component provides this information in the context.
+---------------+
| A |
| Provide |
| Context |
+-------+-------+
|
+--------+--------+
| |
| |
+-------+------+ +-----+-------+
| | | |
| B | | D |
| | | |
+--------------+ +------+------+
|
+------+------+
| |
| E |
| |
+------+------+
|
+------+------+
| C |
| Consume |
| Context |
+-------------+
Use cases for React context is mostly for application wide state and for example - imagine your React application has a theme for a color set and various components in your application needs to know about the theme to style themselves - that's where React's Context is very popular.
+----------------+
| |
| A |
| |
| Provide |
| Theme |
+--------+-------+
|
+---------+-----------+
| |
| |
+--------+-------+ +--------+-------+
| | | |
| | | |
| B | | D |
| | | |
| | | |
+----------------+ +--------+-------+
|
+--------+-------+
| |
| |
| E |
| |
| |
+--------+-------+
|
+--------+-------+
| |
| C |
| |
| Consume |
| Theme |
+----------------+
In this case, component A provides the context with the theme and component C consumes it. Somewhere in between are components D and E and they don't care about the information, only component C consumes it. If any other components wants to consume it, they can access the context as long as the provider is on top of them in the hierarchy.
First, you need to create the React Context itself which gives you access to a Provider and Consumer component. When you create the context with React by using createContext, you can pass it an initial value. The initial value can be null too.
// src/ThemeContext.js
import React from 'react';
const ThemeContext = React.createContext(null);
export default ThemeContext;
Second, component A would have to provide the context with the given Provider component. In this case, its value is given to it right away, but it can be anything from component state (e.g. fetched data) to props. If the value comes from a modifiable React State, the value passed to the Provider component can be changed too.
// src/ComponentA.js
import React from 'react'
import ThemeContext from './ThemeContext'
const A = () => (
<ThemeContext.Provider value="green">
<D />
</ThemeContext.Provider>
)
Component A displays only component D, doesn't pass any props to it though, but rather makes the value green available to all the React components below component A. One of the child components will be component C that consumes the context eventually.
Third, in your component C, below component D, you could consume the context object. Notice that component A doesn’t need to pass down anything via component D in the props so that it reaches component C.
// src/ComponentC.js
import React from 'react'
import ThemeContext from './ThemeContext'
const C = () => (
<ThemeContext.Consumer>
{value => (
<p style={{ color: value }}>
Hello World
</p>
)}
</ThemeContext.Consumer>
)
The component can derive its style by consuming the context. The Consumer component makes the passed context available by using a render prop. As you can imagine, following this way every component that needs to be styled according to the theme could get the necessary information from React's Context by using the ThemeContext's Consumer component now. You only have to use the Provider component which passes the value once somewhere above them.
When should you use React Context? Generally speaking there are two use cases when to use it:
without bothering components in between. 2. When you want to have advanced state management in React with React Hooks for passing state and state updater functions via React Context through your React application. Doing it via React Context allows you to create a shared and global state.
In the next part of this mini tutorial I will show how to implement React context using Reacts Hook useContext and function component. It's very simple and gives us clean code.