If you are somewhat familiar with React then you know that we pass data via props in a top-down manner (from parent to child). But sometimes it becomes difficult to pass data down through the component tree. This mainly occurs when we need to use data in many places, in which case we need global data. This is where Context comes into play. Context provides us the flexibility to pass data to all required places without having to pass the data manually to every level of the component tree.
First of all, don’t try to use Context for every type of data in your React application. It should only be used when we need global data or data which is used in many levels of the component tree, so that we don’t need to pass it through all levels manually, and it becomes easier to manage. If you are in coding then you must be familiar with when to use global variables so you know to use Context when we need such type of data in React. Avoid using Context for all data since it increases the complexity and makes unit testing difficult. Some of the cases when we can use Context are when we need a global state, to manage the theme, language, or some configuration, or when we need location, user data, etc.
Example:-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class App extends React.Component {
render() {
return <newComponent theme="light" />;
}
}
const newComponent = (props) => {
/* here we need to pass theme prop down to
ChangeTheme component so we are passing it
down two levels */
return (
<>
<ChangeTheme theme={props.theme} />
</>
);
}
class ChangeTheme extends React.Component {
render() {
return <Button theme={this.props.theme} />;
}
}
Since we need to pass the theme prop down to the last child and the theme might be useful in many other places, we can use Context here. We will reduce the headache of manually passing in this case.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/* create a context for theme */
const ContextForTheme = React.createContext('dark');
class App extends React.Component {
render() {
/* We need a Provider about which we will discuss shortly */
return (
<ContextForTheme.Provider value="light">
<newComponent />
</ContextForTheme.Provider>
);
}
}
/* we don't need to pass theme prop now in this
intermediate component */
const newComponent = () => {
return (
<>
<ChangeTheme />
</>
);
}
class ChangeTheme extends React.Component {
/* We need to use initial theme which is light in our case then
React find nearest Provider and use its value */
static contextType = ContextForTheme;
render() {
return <Button theme={this.context} />;
}
}
As discussed above we should not use Context for all data since it makes it difficult to reuse components. If we only want to make sure not to pass props through many levels, React provides another, simpler solution called component composition (you can look into it in the React official documentation).
In React, Context is implemented as an API. We need to follow a fixed pattern to use Context, this pattern can be described through the following steps:-
Step 1: First we need to create a Context which can be done using the createContext method.
const newContext = React.createContext(defaultValue);
Step 2: Then we need a Context provider and to wrap our created Context in it. Also, we need a value to put in our Context provider such that we can use it as a value prop.
1 2 3
<newContext.Provider value={/* your value */}> /* ... */ </newContext.Provider>
Step 3: Finally we can use or read our value using Context Consumer or use contextType to consume the value (if you have only a single component in which you want to pass the value).
1
2
3
<newContext.Consumer>
{contextValue => /* based on context value we can render something */}
</newContext.Consumer>
Example:-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* step 1: create a context, in this we can provide a default value by passing a value as parameter when creating context or we can left it empty */
const MyContext = React.createContext('default-value');
class App extends React.Component {
render() {
/* step 2: Make a Provider for our Context*/
return (
<MyContext.Provider value="change-value">
<middleComponent />
</MyContext.Provider>
);
}
}
/* intermediate component */
const middleComponent = () => {
return (
<>
<MyComponent />
</>
);
}
class MyComponent extends React.Component {
/* Step 3: make Consumer to consume the context value */
render() {
return (
<MyContext.Consumer>
{
contextValue => {
return <div>context value = {contextValue}</div>
}
}
</MyContext.Consumer>
)
}
}
We have used Consumer in the above example, which can work on multiple components. In the previous example, we used contextType which is simple to use, but cannot be used with multiple components and also only works inside a class component.
If you are a Hook lover then you can use useContext() hook to implement the context functionality in your functional component. To do this we go through the above three steps to implement context, the only difference is we use useContext() for reading the value in place of consumer or contextType.
const value = useContext(MyContext);
Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/ values
const ListofThemes = {
dark: {
foreground: "#ffffff",
background: "#222222"
},
light: {
foreground: "#000000",
background: "#eeeeee"
}
};
// step 1: create context
const ContextForTheme = React.createContext(ListofThemes.dark);
//step 2: create Provider for context
function App() {
return (
<ContextForTheme.Provider value={themes.dark}>
<newComponent />
</ContextForTheme.Provider>
);
}
//middle component
const newComponent = (props) => {
return (
<div>
<ChangeTheme />
</div>
);
}
// step3: use useContext to read or consume values
const ChangeTheme = () => {
const currentTheme = useContext(ContextForTheme);
return (
<button style={{ background: currentTheme.background, color: currentTheme.foreground }}>
Change Theme
</button>
);
}
We can say that using Context is very simple in React. We just need to follow some steps which are similar for both functional and class components. We need to make sure we know when to use Context in React. If it is not necessary then we should not use Context. React also provides us with Redux, which is an independent tool from React, but for simple global data management, we should use Context.