What is context ?
In laymen term, Context means “the situation in which something happens or that caused something to happen”.
Significance of context in React
As per above definition, React Context will provide an encapsulation where in alteration of a variable happens and we access the same variable within the same context. Important thing to note here is that this encapsulation can contain multiple components and thus can help in variable sharing between components or variable propagation from top to bottom components or vice versa.
Let’s try to implement a basic scenario to understand this in better way.
Use Case
We are going to implement a basic counter application.
After user clicks on button — Increase Counter, Counter value increases by 1.
Important thing to note here is that the button and the header displaying the value are in different component and we are sharing the value in real time between components.
Let’s see how to implement the use case
- First we will do the basic scaffolding of react application
npx create react-app counter template=typescript
Above command will create one basic React application.
Next we will create two basic components.
Header Component
// Header.tsx
const Header = () => {
return(
<h1>Counter value is </h1>
)
}
export default Header;
Body Component
// Body.tsx
const Body = () => {
const incCounter = () => {
}
return(
<button onClick={incCounter}>
Increase Counter
</button>
)
}
export default Body
Update App.tsx by removing all the code in return block with
// App.tsx
return (
<>
<Header/>
<Body/>
</>
);
We have basic structure in place and now we will dive in implementing context.
- Create a file named as CounterContext.tsx and add below code
import { createContext, useState } from "react";
// Step 1
const CounterContext = createContext({
counter : 0,
handleCounter : () => {}
});
// Step 2
const CounterProvider = ({children}:{children:any}) => {
// Step 3
const [counter,setCounter] = useState(0);
//Step 4
const handleCounter = () => {
setCounter((prevValue:any) => prevValue + 1);
}
//Step 5
const counterObj = {counter,handleCounter}
// Step 6
return(
<CounterContext.Provider value={counterObj}>
{children}
</CounterContext.Provider>
)
}
// Step 7
export { CounterProvider , CounterContext}
Don’t get intimidated by so much lines of code as this is the basic crux of context and we will go through each and every line step by step.
- In Step 1 we have created the context using createContext named as counterContext. createContext is method which is provided by react which helps in creating a context within which all the components are encapsulated where in there is a need to access or share variables.
Another thing to notice here is that createContext should expose the variables and methods which we need to access in encapsulated components, for e.g. in our example counter variable and handleCounter method are exposed. - Context created in Step 1 has a Provider react component. Advantage of using this Provider is that it gives a wrapper around which we can wrap all the components where in we want to subscribe for context changes. Here in Step 2 we have created a function named as CounterProvider which returns a react Provider component.
- In Step 3, we have simply created a counter variable with useState hook and setter for the same using setCounter.
- In Step 4, we have create handleCounter method which will trigger the setter for counter variable.
- Step 5 is the real business where in we define the properties, methods which we need to expose to the components around which we have wrapped Provider. Do note here that the object structure defined in Step 1 should match with the object structure defined here, else you will get a Type mismatch error. You can think of the structure defined in Step 1 as an alias and the one defined in Step 5 as real implementation.
- Provider react component returned in Step 2 needs a value prop where in we pass the object defined in Step 5, so that the same can be available in enclosed components.
- Finally in Step 7 we export both the context and provider created so, the same can be accessed outside.
Through above mentioned step, we are done with the implementation of our CounterContext and in most of the cases these are the basic 7 steps which are needed, tough use case and complexity may vary.
Now, lets use this created context to implement our use case.
- Once we have the CounterProvider component ready, we will use the same in App.tsx and wrap it around Header and Body component as shown below. We will wrap the Provider around the components where in we want to access the context.
return (
<CounterProvider>
<Header/>
<Body/>
</CounterProvider>
);
Let’s make changes in Body component to trigger change of counter variable.
- Body Component
import { useContext } from "react";
import { CounterContext} from './CounterContext';
const Body = () => {
const { handleCounter} = useContext(CounterContext); // Step 1
const incCounter = () => { // Step 2
handleCounter();
}
return(
<button onClick={incCounter}>
Increase Counter
</button>
)
}
export default Body
In Step 1, we are using useContext api provided by React. It accepts an argument which is the name of the context. Once we pass in the context name, it will return the object or property passed in the value prop while returning Provider component. In our case, it will be like —
<CounterContext.Provider value={counterObj}>
{children}
</CounterContext.Provider>
We get handleCounter property using destructuring .
2. In Step 2, we are calling the handleCounter on click of the button. If you recall the handleCounter method is the one where in we are setting the new counter value with setCounter. So, while clicking on Button the counter variable in the context is updated.
Now, let’s make changes in header component to subscribe the changes made to counter variable
- Header Component
Once the value is updated by Producer we need to consume the same value in other component. Let’s make some changes in Header component for the same.
import { useContext } from "react";
import { CounterContext } from "./CounterContext";
const Header = () => {
const { counter } = useContext(CounterContext); // Step 1
return(
<h1>Counter value is - {counter}</h1> // Step 2
)
}
export default Header;
- In Step 1 we are again using useContext api and this time we are getting counter variable which is the updated value after click of button in Body component.
- In Step 2 we are displaying the counter variable.
This in nut shell is how we can use Context and useContext api to pass value from one component to other in React.
Do, let me know in response if you have any further queries.