According to the official React documentation, “A Context provides a way to pass data through the component tree without having to pass props down manually at every level”.
Context solves the problem of passing data to any level of nested children. When doing this using props, we would require to resort to prop drilling i.e. pass the props through each intermediate child manually, even though the child does not use the data.
Context is like a pipe into which once the data is pushed, it remains intact and can be accessed by any child at any level within the context of the parent.
Multiple instances of the context can be created by providing different values to it.
In this blog, we will go through the following:
- The different ways in which we can use Context in React applications i.e. how we can provide data into the Context, and
- How we can consume data from the Context at any level of the hierarchy.
You can read other blogs on React from our experts here.
Contact us to connect with our React Experts and learn more about Context or get technical advice on using this React Toolset in your applications.
To know more about WalkingTree Technology’s offerings and services to React, visit our Technologies page.
Step 1. Creating the Context
1 |
const SeasonContext = React.createContext(); |
This returns an object with two values {Provider, Consumer} using which data can be pushed to and received from the Context pipe respectively.
Step 2. Adding Data to the Context
Using the default Value –
The context can be created with a default value which will be used if no value is explicitly passed to the Provider.
1 |
const CityContext = React.createContext(“Hyderabad”); |
Using the Provider –
Once the context is created, it can be provided with data by using the Context. Provider component. The context can be provided with a value passed as a prop to the Provider.
1 2 3 4 5 6 7 8 9 |
Const App = () => { return ( <SeasonContext.Provider value={“winter”}> <div> <Season /> </div> </SeasonContext.Provider> ); } |
A value is a special property on the Provider component which will enable the placement of data into the Context. The data can be of any format, a simple constant, an object, an array, or a method reference as well.
Step 3. Reading Data from Context
Now, once the data is available in the pipe, any child (within the hierarchy) can get the data using one of the following approaches:
Using contextType –
By assigning the contextType to the current context, the value within the context becomes available using ‘this.context’.
1 2 |
static contextType = SeasonContext ; const text = this.context === “summer”? 'It’s Hot!' : 'It’s cold'; |
This approach can be used only if the component needs to extract data from a single Context but not from different/multiple Contexts. In such cases, we can use the other approaches mentioned below.
Using Consumer
Context.Consumer can be used to extract the value from the context by wrapping the component which needs access to the value in the context.
1 2 3 |
<SeasonContext.Consumer> {(season) => this.getSeason(season)} </SeasonContext.Consumer> |
Suppose the component is also dependent on other contexts, like CityContext and MonthContext, our code will look something like this:
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 |
const getSeason = (city, month, season) => { console.log(city, month, season); if (month > 2 && month < 9) { return city === "Hyderabad" ? "summer" : "winter"; } else { return season; } }; const SeasonCard = props => { return ( <CityContext.Consumer> {city => ( <MonthContext.Consumer> {month => ( <SeasonContext.Consumer> {season => { return <h1>{getSeason(city, month, season)} is here</h1>; }} </SeasonContext.Consumer> )} </MonthContext.Consumer> )} </CityContext.Consumer> ); }; |
How does the above code look? Not very clean, right?
With so many tags nested around the elements just to get the context values, it can look clunky.
Get in touch with our coding experts to get ideas on enhancing your Context code today.
Using useContext hook
React hooks introduced a new hook- useContext to solve this issue and make the code look cleaner and clearer to understand. So, if you are using React version 16.8.0 or above, you can safely use this approach.
The useContext hook simplifies the Context consumption process. The same logic has been implemented here using the useContext hook.
1 2 3 4 5 6 7 8 9 10 |
const SeasonCard = props => { const season = useContext(SeasonContext); const city = useContext(CityContext); const month = useContext(MonthContext); const finalSeason = getSeason(city, month, season); return ( <h1>{finalSeason} is here</h1> ); }; |
This code looks much better now. I’m sure you agree as well.
Now, we know how to use Context to provide data to the context pipe and then consume it anywhere within the nested children.
Check the complete code on sandbox.
You can fork this and try out different variations if you want.
Context vs Redux
Another question came up with the advent of the React Context which was able to handle state globally and share it across components.
The imperative question is:
Can Context replace Redux?
What capabilities does React-Redux provide? Here is your answer,
-
- Global store to handle all the application-level states
-
- Reducers to modify the state by dispatching actions
-
- Middleware to handle asynchronous actions
We can try to achieve the Redux store functionality with Context since we can pass any data as a part of Context.
Let’s understand this with an example.
We can set up a store Provider for the Context, which needs to be global and the value of the context can be changed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
const Context = React.createContext(); export class CityCxtStore extends React.Component { state = { city: "Hyderabad" }; onCityChange = city => { this.setState({ city }); }; render() { return ( <Context.Provider value={{ ...this.state, onCityChange: this.onCityChange }} > {this.props.children} </Context.Provider> ); } } |
In the above code, the value of the state (city in this example), as well as, the method reference to modify state is provided as a part of context and can be used in the Child components.
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 |
import CityContext from "../contexts/CityContext"; class CitySelector extends React.Component { static contextType = CityContext; cityList = ["Hyderabad", "Newyork", "Istambul", "Colombo"]; render() { return ( <div class="ui fluid search selection dropdown"> <div class="default text">Select City</div> <ul> {this.cityList.map((item, index) => { return ( <li class="item" key={index} onClick={() => this.context.onCityChange(item)} > {item} </li> ); })} </ul> </div> ); } } export default CitySelector; |
Here, the child can access the state from the global store and also set the state using the method reference included in Context.
In this way, we can achieve a Redux-like global store, with the capability to get the latest state value and also the ability to change the state value in store using Context.
Check the complete code in sandbox.
CONCLUSION
React Context is a useful tool to share and access the state across multi-level nested children.
Context can be used to replace Redux for simple stores where the value does not change frequently in place of Redux, but, Redux remains the preferred tool for state management due to its well-defined architecture and a myriad of capabilities. If you are yet to learn Redux, Context can surely come in handy for managing the global state of your application.
Call us today to learn more about this amazing React Toolkit, or let our experts help you with your React Application.