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;