The Context API is a component structure provided by the React framework, which enables us to share specific forms of data across all levels of the application. It’s aimed at solving the problem of Prop drilling.
Prop drilling (also called “threading”) refers to the process you have to go through to get data to parts of the React Component tree.
Before the Context API, we could use a module to solve this, which led to the increasing popularity of state management libraries like Redux. Libraries like Redux allows you to get data from the store easily, anywhere in the tree. However, let’s focus on the Context API.
As with most component-based frontend frameworks, passing some form of data from one component to another is usually a real need. Typically it comes in the form of passing data from a parent to a child component or even child to parent components. This leads to components having data they don’t actually need, but they need to pass on down the tree.
This gets cumbersome pretty fast especially for certain types of props (e.g locale preference, UI themes, language settings etc) that are required by many different components within an application. The Context API aims to solve this problem, and provides a way to share data values like this between components without having to pass a prop through every level of the app tree.
The Context API has actually always been there but was considered experimental. Moving forward the API was improved to stability, and as of the release of version 16.3, the feature was made available and subsequently considered a new addition to the clan of features that make React a wonderful framework.
Before now many of the tools, we have been used to, like react-redux, react-native-flux, MobX-react, and react-router all used context to function, so you were probably already using and loving it, even if not directly.
import React from 'react'
// This is the equivalent to the createStore method of Redux
// We can create it in a separate file or in a sharered file with a component
// As long as we can export it
const MyContext = React.createContext()
export default MyContext
import React, { Component } from 'react'
import appContext from 'utils/appContext'
const { Provider } = appContext
export default class AppState extends Component {
state = { number: 1 } // Inital state like Redux
mutNumber = () => {
this.setState({
...this.state,
number: this.state.number + 1,
})
}
render() {
return (
<Provider
value={{
state: this.state,
actions: {
mutNumber: this.mutNumber,
},
}}
>
{this.props.children}
</Provider>
)
}
}
import React from 'react'
import { render } from 'react-dom'
import AppState from './state'
import App from './App'
render(
<AppState>
<App />
</AppState>,
document.getElementById('root')
)
import React, { Component } from 'react'
import appContext from 'utils/appContext'
const { Consumer } = appContext
export default class MyChildComponent extends Component {
render() {
return (
<Consumer>
{({ state, actions }) => (
<div>
<span>{state.number}</span>
<button onClick={actions.mutNumber}>+</button>
</div>
)}
</Consumer>
)
}
}
import React from 'react'
import appContext from 'utils/appContext'
export default function MyChildComponent(props) {
const value = React.useContext(appContext)
const { state, actions } = value
return (
<div>
<span>{state.number}</span>
<button onClick={actions.mutNumber}>+</button>
</div>
)
}
import React, { Component } from 'react'
import appContext from 'utils/appContext'
export default class MyChildComponent extends Component {
static contextType = appContext
render() {
return (
<div>
<span>{this.context.state.number}</span>
<button onClick={this.context.actions.mutNumber}>+</button>
</div>
)
}
}