Getting Started with React Context API
React Context was designed to provide a way in which data could be stored and accessed by various components.
Why would you need this?
This feature becomes important when props would have to be passed to deeply nested or numerous components within an application.
In react applications, data is usually passed from parent to child (and grandchildren) components via props.
Let’s take a look at the following example. Let’s say we have two components – Nav
and Link
// link.js - Link component
import React from 'react';
export default props => {
<a
href={props.link}
title={props.title}
>
{props.linkName}
</a>
}
// nav.js - Nav component
import React from 'react';
import Link from 'path/to/link.js';
export default () => (
<nav>
<h1>Page Links</h1>
<Link
link='/home'
title='Homepage'
linkName='Home'
/>
<Link
link='/about'
title='About'
linkName='About'
/>
</nav>
)
There is no difficulty in doing this because Link
is the immediate child of the Nav
component. Imagine Nav
becomes a deeply nested child of another component, say Layout
, we would have to pass props all the way down to that child before we get our desired UI.
This is where React Context comes in.
A general example of which this can be applied is in UI themes. You wouldn’ t want a theme handler button deeply nested within components to manipulate theme by going through many props.
That would also result in various components (like page components) going through different other components to get access to the current theme.
Let’s start using React Context API
We would be taking the UI theme scenario as our project example.
Let’s say we have our theme button handler to be;
import React from 'react';
export default () => (
<button onClick={props.themeHandler}>
Change Theme
</button>
)
Instead of having to use this component in a Header component which would be a child of a Layout component (managing the theme), we could have a store that manages the theme while other components work with it.
The React Context API:
React.createContext
const MyContext = React.createContext(defaultValue);
This creates a context object. It has a default-value argument that is read if the component does not have a matching Provider.
Context.Provider
<MyContext.Provider value={/* some value */}>
The Provider component holds the value which consumer components would use. Whenever the value changes, the consumers are re-rendered.
For subscribing to contexts, we have the property, contextType
for classed components and a consumer
component for functional components.
Class.contextType
For example,
class ExampleClass extends React.Component {
render(){
let value = this.context;
// return anything based on the context
}
}
ExampleClass.contextType = MyContext;
// MyContext is the context which you have chosen
Context.Consumer
For example,
<MyContext.Consumer>
{value => {
return (
// return something based on the context value
)
}}
</MyContext.Consumer>
This component requires a function as a child that receives the value as an argument and returns a React node.
Check out the React Context documentation for more information.
UI Theme project example
Let’s get to understand React Context with the following project. You could practice along by getting a react project. Follow this documentation to do so.
First, let’s manage the context in a separate file
// themeContext.js
import React from 'react';
let themeList = {
lightMainColor: 'blue',
darkMainColor: 'black'
}
let ThemeContext = React.createContext(
themeList.darkMainColor
// default value
)
export { themeList, ThemeContext }
Here, we have a list of theme colors and the context theme object we created. Remember that the argument passed there is the default value to be used if no provider is found.
Next, we customize our theme handler.
import React from 'react';
import { ThemeContext } from './themeContext';
export default props => (
<ThemeContext.Consumer>
{
value => (
<button style={{color: value}} onClick={props.themeHandler}>
Change Theme to {value}
</button>
)
}
</ThemeContext.Consumer>
)
Also, remember that this is the functional component way of subscribing to a context. The class component syntax shared earlier in this article could be used to do the same.
In our code above, we subscribe to the context and render a button with the color saved in the context and the value itself ({value}
)
Lastly, let’s create an App
component
import React from 'react';
import { ThemeContext, themeList } from './themeContext';
import ThemeHandler from './theme-button-handler';
class App extends React.Component {
state = {
theme: themeList.lightMainColor
}
changeTheme = () => {
let prevTheme = this.state.theme;
let newTheme = prevTheme === themeList.lightMainColor ? themeList.darkMainColor : themeList.lightMainColor;
this.setState({
theme: newTheme
})
}
render() {
return (
<header>
<h1>Theme Context</h1>
<ThemeContext.Provider value={this.state.theme}>
<ThemeHandler themeHandler={this.changeTheme}/>
</ThemeContext.Provider>
<ThemeHandler themeHandler={this.changeTheme}/>
</header>
);
}
}
export default App;
If you were following along with practicing, you’d discover that we have a header element (Theme Context) with two buttons.
The first button is labeled Current: blue. Change Theme
and the other, ‘Current: black. Change Theme
.
You’d also notice that clicking on the first button changes the color to black while the other does nothing on click.
The reason is,
Our context provides a default value which is the color of the dark theme in our theme list. The first button has a provider with a value (which is the current state) that is why we have blue. As for the second button, there is no provider, so it uses the default value in the context. This is the same reason why even when the second button changes the state, it still uses the current value in the context.
Conclusion
From our example project, we can see the importance of React Context. We do not need to pass down several props for components to use data. Also, with the location of the context, different components can make use of the data.
Did you observe that working with Context is not really easy to work with?
You observed right. Hence, it should be used only when needed so as to maintain readability all through your application.