The Realm of Context API in React
This is a concise walkthrough on Context API in React.
One of the key concepts in React is state and props, I don't believe you can be a successful React developer without a solid understanding of both state and props. Managing state
in React can be a bit of an issue, especially in medium or large projects, but thankfully we have ContextAPI to save the day. In this article we will be going over:
What Context API is and Why you might need it.
How ContextAPI works
How to set up and use ContextAPI in your projects.
Best practices when using ContextAPI.
The disadvantage of ContextAPI.
Are you ready to get better? if your answer is yes then let's dive in.
Prerequisites
To experience the entire flavor of this article, some important concepts are required:
Understanding of
state
andprops
in React.How to create components in React.
Building some projects or a significant amount of code practice with React.
What is Context API and Why you might need it
If you're familiar with React, you probably know about state management and props drilling. As the project we're building grows, (i.e. the architecture of the project grows different branches) it becomes harder to manage the state across the different branches of the architecture. A common example of this problem is encountered when you have a state in a parent component and also need that same state in a child component.
If we look at the image above, let's say we have a state in the Root App and we also need it in Child1 now you will have to move from the Root App --->Parent2--->Child1. This is a good example of props drilling, because if we defined the state in the Root App when we want to access this state in the other components we pass the state as props, so props drilling pass down props to all level if we have a hierarchy of components like what we have in the image above. Let's take a look at what prop drilling means with some code snippets and images:
import { useState, createContext, useContext } from "react"
import './App.css'
let Contextexample=createContext(null)
export default function Parent(){
let [username,setusername]=useState("Sicario")
return(
<div className="Parent">
<h2>{username}</h2>
<Child1 setusername={setusername}/>
</div>
)
}
export const Child1=({setusername})=>{
return(
<div className="Child1">
<Grandchild setusername={setusername}/>
<button onClick={()=>{
setusername("Siacrio001")
}}>Child1 btn</button>
</div>
)
}
export const Grandchild=({setusername})=>{
return(
<div className="Grandchild">
<button onClick={()=>{
setusername("Sicario020")
}}>Grandchild</button>
<br />
</div>
)
}
From what you can see above we had to pass the state from the Parent-->Child1-->Grandchild, this is what prop drilling means, we are passing the state as a prop to both "Child1" and "Grandchild".This becomes a problem if we have a lot of components that need the state(i.e. as the project architecture becomes bigger props drilling becomes the less desired option) and it also makes your code less organized. The Context API helps to solve this kind of problem, the Context API is a state management solution provided by React. If you are just hearing about Context API for the first time, you might think you will be making an API call from an endpoint that will return a JSON string but that's not quite the case with the Context API(That was what I thought too so don't be ashamed)
How ContextAPI works
The basic job of ContextAPI as a whole is to help pass down data from the top to bottom in the components tree without having to pass props at every level. ContextAPI avoids passing data from Parent-->Child-->Grandchild. A real-life illustration of this will be the Grandchild has the Parent contact directly which he can call when he needs anything without the Child necessarily knowing about it(I am sure the Grandchild is happy about that).
A visual representation of how ContextAPI works.
The ContextAPI has two main parts: Contextprovider and Contextconsumer, the names are self-explanatory, the Contextprovider provides and stores the values you need and acts like a Parent while the Contextconsumer helps to extract the values you want from the Contextprovider.
How to Use Context API in Your Projects
You now know what props drilling means and why you might need to use Context API, let's go over how you can and use it.
Setting up the Context API
You can set up the Context API with the following steps:
Import
CreateContext
anduseContext
hook, it will look like this:import { useState, createContext, useContext } from "react"
Create a global Context variable with any name with this command:
let Contextexample=createContext(null)
let Contextexample=createContext(null)
Create a Context Provider and wrap it around what you want access to like this:
export default function Parent(){ let [username,setusername]=useState("Sicario") return( <Contextexample.Provider> <div className="Parent"> <h2>{username}</h2> <Child1 setusername={setusername}/> </div> </Contextexample.Provider> ) }
Pass a value property to the Context provider and include the value you want access to, like this:
export default function Parent(){ let [username,setusername]=useState("Sicario") return( <Contextexample.Provider value={{username,setusername}}> <div className="Parent"> <h2>{username}</h2> <Child1 setusername={setusername}/> </div> </Contextexample.Provider> ) }
Delete the props in the Child Components in this case; Child1 and GrandChild components because we don't need it anymore.
Use the
useContext
hook to specify the value from the Context Provider you want the Child component to have access to, theuseContext
hook is the consumer that helps us extract the values you want from the Context Provider to the Child components, this phase can be referred to as consuming the Context, from the example we are using, it will look like this:export const Child1=({setusername})=>{ const {setusername}=useContext(Contextexample) return( <div className="Child1"> <Grandchild setusername={setusername}/> <button onClick={()=>{ setusername("Siacrio001") }}>Child1 btn</button> </div> ) } export const Grandchild=({setusername})=>{ const {setusername}=useContext(Contextexample) return( <div className="Grandchild"> <button onClick={()=>{ setusername("Sicario020") }}>Grandchild</button> <br /> </div> ) }
After following the steps above, the code will now look like this:
import { useState, createContext, useContext } from "react"
import './App.css'
let Contextexample=createContext(null)
export default function Parent(){
let [username,setusername]=useState("Sicario")
return(
<Contextexample.Provider value={{username,setusername}}>
<div className="Parent">
<h2>{username}</h2>
<Child1 setusername={setusername}/>
</div>
</Contextexample.Provider>
)
}
export const Child1=({setusername})=>{
const {setusername}=useContext(Contextexample)
return(
<div className="Child1">
<Grandchild setusername={setusername}/>
<button onClick={()=>{
setusername("Siacrio001")
}}>Child1 btn</button>
</div>
)
}
export const Grandchild=({setusername})=>{
const {setusername}=useContext(Contextexample)
return(
<div className="Grandchild">
<button onClick={()=>{
setusername("Sicario020")
}}>Grandchild</button>
<br />
</div>
)
}
In our example, the child components and parent components are defined on the same Contextexmple.jsx
file, but if you have different components on different files it's the same process we just have to import where necessary.
Best Practices when Working with ContextAPI
Like every other concept in programming, they are some practices you employ to make the code well organized. This section will go over some of those practices, and they include:
Creating a component for each Context provider
One very useful practice when working with ContextAPI is to create a component for each Context provider you create, let's say you have a social media website, you might need some user information throughout the website. Information like username, profile picture or user-id. In this case, here is what you do:
Create a Context outside the component so you can access it globally, like this:
export const InfoContext=createContext(null)
(the InfoContext can be any other name but it's ideal to include "Context" in the name you choose to use)Create the Context provider component, in this case, it's "InfoContextPovider"(pass one prop) which will include the functions and values you might need, I will use some dummy code to show the structure:
import { useState,createContext } from "react"; export const InfoContext=createContext(null) export const InfoContextProvider=({details})=>{ const [userinfo,setuserinfo]=useState(null) const [isAuth,setisAuth]=useState(null) const signin=()=>{ fetch("/signin").then((res)=>{ setisAuth(true) setuserinfo(res.user) }) } const signout=()=>{ fetch("/signout").then((res)=>{ setisAuth(false) setuserinfo(null) }) } const value={ userinfo, setuserinfo, isAuth, setisAuth, signin, signout } return ( <InfoContext.Provider value={value}>{details}</InfoContext.Provider> ) }
Pass the value to the Provider and then wrap the Provider around what you want to have access to it as seen above on line 30
From the image above, you can see that this practice makes your code more organized and well-structured.
Use ContextAPI only when necessary
When dealing with state management in React, ContextAPI should not be the first option you consider because it's not necessary in all cases. You can use props to pass down data through your component tree, however; the discretion to determine whether it's necessary or not depends on experience and practice(this is where senior devs have the advantage). One way to go about this is if you have some data that is needed by various nested components, in that case, you can go ahead and use ContextAPI.
Make use of Default Values
When creating a Context it's advisable to use default values, so in the case that the provider doesn't need to give the value to a particular component the default value acts like a placeholder value. In the code example, above the default value used for the Context is
null
.
Disadvantages of ContextAPI
We have been discussing all the sweet things and advantages of ContextAPI but like every other thing ContextAPI has its disadvantages, so let's talk about that:
Rerender
When one single value of the Context changes the whole Context changes i.e. every component that the Context provider is wrapped around will have to rerender even if the component does not access the value that is changing. This is a bit unnecessary and it slows the performance of the website, it might not be an issue with small projects but when you have a big project this feature of the ContextAPI becomes a serious issue.
Conclusion
I'm glad you stayed till the end, in this article we have gone over some really interesting things about Context API as well as its disadvantage so let's do a quick recap:
Some keynotes to take away from this article are:
The Context API is a state management solution provided by React.
The Context API is an alternative to props drilling.
Make use of Context API when it's only necessary instead, you can use props drilling (it is not entirely bad).
Use default values when Creating your Context.
Another state management alternative available is: Redux and Redux toolkit you can check it out.
"Genkai wo Koero" - Surpass Your Limits.