Have you ever passed a property to a React component for no other reason but just to be able to pass it down to a child of that component? Well, this is exactly what the new React Context API tries to fix.
note: this a repost from the original Hackernoon article that can be found here. If you like this article sign up for my email list so I can share with you the next screencasts and tutorials!
Prefer Video?
Prefer video tutorials? I made this tutorial also into a video, which can be found below:
The problem?
For example in the example below:
- we have some data, namely a number with the value of 10
- we need the data in the Red component and also in the Green one
- the Green component is a child of the Blue component that is a child of the Red component
- so, most probably, we will need to send the data from the Red component to the Blue one just to be able to send it to the Green one
In this point our code would look something like this:
const Green = (props) => (
<div className="green">{props.number}</div>
)
const Blue = (props) => (
<div className="blue">
<Green number={props.number} />
</div>
)
class Red extends Component {
state = {
number : 10
}
render() {
return <div className="red">
{this.state.number}
<Blue number={this.state.number} />
</div>
}
}
We have to send the data to the Blue component only to “drill” it down to the Green component. And this is a simple case. Imagine what will happen if we have ten levels of parent-child React components.
Till React 16.3 the standard solution to problems like this one was Redux or Mobx or any other library that deals with state management. But now, we have the solution embedded into React.
The solution: state management with React Context?
What React Context is allowing us to do is to define data stores and access them where they are needed. We don’t have to pass down data through properties any more. With React Context we can define something like an “application global state” and use that data where needed.
Who to use React Context ?
There are two main steps to setup the React context into your application :
- setup a context provider & define the data you want to store
- use a context consumer where ever you need the data from the store
In order to make the context provider we will need to make a context via React.createContext. We will call our context AppContext:
const AppContext = React.createContext()
The newly created AppContext will be used to build a context provider component. This provider will store, in its state, the data we need and it will wrap all of the content of the Red component:
class AppProvider extends Component {
state = {
number : 10,
}
render() {
return <AppContext.Provider value={this.state}>
</AppContext.Provider>
}
}
//...
class Red extends Component {
render() {
return <AppProvider>
<div className="red">
<Blue />
</div>
</AppProvider>
}
}
Long story short: by wrapping everything in this AppProvider we can easily inject the data from the value attribute where needed. Given the fact that AppProvider will be used as a wrapper component it’s important to use the {this.props.children} in the render method.
Now, with the provider installed if we want to access some data from the provider we can easily use the context consumer.
<AppContext.Consumer>
{(context) => context.number}
</AppContext.Consumer>
All the data that we added to the value property of AppContext.Provider is now made available by the context parameter of the arrow function.
At this point our code will look something this
const AppContext = React.createContext()
class AppProvider extends Component {
state = {
number : 10
}
render() {
return <AppContext.Provider value={this.state}>
{this.props.children}
</AppContext.Provider>
}
}
const Green = () => (
<div className="green">
<AppContext.Consumer>
{(context) => context.number}
</AppContext.Consumer>
</div>
)
const Blue = () => (
<div className="blue">
<Green />
</div>
)
class Red extends Component {
render() {
return <AppProvider>
<div className="red">
<AppContext.Consumer>
{(context) => context.number}
</AppContext.Consumer>
<Blue />
</div>
</AppProvider>
}
}
Please note that we are not anymore passing down the number property to the Blue component or the Green one anymore. All of this data is now handled by the React Context mechanism.
Using actions and modifying data in the React Context
Unless you are working on a very basic app you will need a way to update/change the data that comes from the React Context. The minimal example can be a button that will increment the number from our data.
We will need something that is the alternative from the Mobx or Redux actions.
This is quite easy to achieve. What we will to do is to define a function on the state of the AppProvider context, and do the required updates onto the state data.
class AppProvider extends Component {
state = {
number : 10,
inc: () => {
this.setState({number: this.state.number + 1})
}
}
//...
}
Having the action defined we can use it through a AppContext.Consumer and call it in a onClick event:
const Blue = () => (
<div className="blue">
<AppContext.Consumer>
{(context) => <button onClick={context.inc}>INC</button>}
</AppContext.Consumer>
<Green />
</div>
)
Our final code will now look something like this :
import React, { Component } from 'react'
const AppContext = React.createContext()
class AppProvider extends Component {
state = {
number : 10,
inc: () => {
this.setState({number: this.state.number + 1})
}
}
render() {
return <AppContext.Provider value={this.state}>
{this.props.children}
</AppContext.Provider>
}
}
const Green = () => (
<div className="green">
<AppContext.Consumer>
{(context) => context.number}
</AppContext.Consumer>
</div>
)
const Blue = () => (
<div className="blue">
<AppContext.Consumer>
{(context) => <button onClick={context.inc}>INC</button>}
</AppContext.Consumer>
<Green />
</div>
)
Still in its early phase React 16.3 Context API can be used as an alternative to the classic state management alternative if your sole purpose of using a state management library is avoiding prop drilling.
I hope you found this article helpful! Let me know if there are other things you’re would want to learn in the realm of React, Mobx and Javascript.
If you liked this article sign up for my email list so I can share with you the next screencasts and tutorials!
Top comments (2)
AppContext is not defined when the components are on different files
The same issue occurred for me. But I found the solution also.
Create a file AppContext.js separately.
//AppContext.js
import React from "react";
const AppContext = React.createContext();
export default AppContext;
Now import this file where AppContext is required.