React/ReactJS: Context API
In React, data, state and methods are usually passed from a parent component to its child manually via props. This actually is some sort of prop drilling, passing props from higher to lower components in the tree.
We recap it with an example below.
A string value is being passed from the component <GuardiansOfTheGalaxy/>
to its descendant <Groot/>
explicitly as name='Groot'
attribute-value pair. This passed value is accessed via the props
object as {props.name}
inside the child component <Groot/>
.
import React from "react";
const GuardiansOfTheGalaxy = () => {
return (
<div>
<Groot name="Groot"/>
</div>
);
}
const Groot = (props) => {
return (
<div>I am {props.name}.</div>
);
}
export default GuardiansOfTheGalaxy;
The above code renders the following:
Now there is a way to achieve this without explicitly passing props down the component tree. React has the Context API for the purpose. In this tutorial, we will look at this React Context API along with an example.
React.createContext() creates a Context object which comes with a Provider and Consumer components. The Provider component is passed a value prop which is to be passed to its child components. The descendant Consumer components receives this current context value and returns a React node. Also, if the value passed in the Provider changes, it will re-render in all its Consumer components.
So far as data is concerned, the Context API gives us a provision to avoid using Redux to some extent.
We redo the above code using React.createContext()
. Note that the Provider and Consumer components comes into play out of the created Context object. And we do not pass any props to the child component <Groot/>
.
import React from "react";
const GuardianContext = React.createContext('Gamora');
const GuardiansOfTheGalaxy = () => {
return (
<GuardianContext.Provider value={'Groot'}>
<div>
<Groot/>
</div>
</GuardianContext.Provider>
);
}
const Groot = () => {
return (
<GuardianContext.Consumer>
{(name) => <div>I am {name}.</div>}
</GuardianContext.Consumer>
);
}
export default GuardiansOfTheGalaxy;
There is also an alternative syntax where we can define the Provider and Consumer components separately.
import React from "react";
const { Provider, Consumer } = React.createContext('Gamora');
const GuardiansOfTheGalaxy = () => {
return (
<Provider value={'Groot'}>
<div>
<Groot/>
</div>
</Provider>
);
}
const Groot = () => {
return (
<Consumer>
{(name) => <div>I am {name}.</div>}
</Consumer>
);
}
export default GuardiansOfTheGalaxy;
Now if there are no matching Provider to a Consumer component in the higher heirarchy, it takes the default value passed to the createContext()
function at the time of creation of the Context object. As an example, we remove the Provider component from the above script and the Consumer component is without any Provider above it in the tree. The context value it takes is the default value passed while creating the Context. The program below renders:
I am Gamora.
import React from "react";
const { Provider, Consumer } = React.createContext('Gamora');
const GuardiansOfTheGalaxy = () => {
return (
<div>
<Groot/>
</div>
);
}
const Groot = () => {
return (
<Consumer>
{(name) => <div>I am {name}.</div>}
</Consumer>
);
}
export default GuardiansOfTheGalaxy;
You can also pass functions to the Provider value. Here we pass the function sayHello()
which just logs the simple message "Hello, Groot!
" into the console.
import React from "react";
const GuardianContext = React.createContext('Gamora');
const GuardiansOfTheGalaxy = () => {
return (
<GuardianContext.Provider value={{
name: 'Groot',
sayHello: () => {console.log('Hello, Groot!')}
}}>
<div>
<Groot/>
</div>
</GuardianContext.Provider>
);
}
const Groot = () => {
return (
<GuardianContext.Consumer>
{(value) => (
<div>
I am {value.name}.
<button onClick={value.sayHello}>Say Hello!</button>
</div>
)}
</GuardianContext.Consumer>
);
}
export default GuardiansOfTheGalaxy;