React has the concept of props, we pass props from one component into another. For React Applications, where data is passed in one direction, this can make things confusing, as we would have to continuously pass in props all the way down until we get to the component that needs it. This is referred to as prop drilling.
This often made our React Applications confusing, as it would be had to track down the where our props were coming from when debugging and inconvenient to tell each child component to pass down props that originated from multiple layers upward.
The React Context API allows us to define props at the highest level using a Provider and bring down the props we need from the provider by importing a Context component to the specific component that needs those props. This prevents us from passing those props through components that don’t need them.
The first thing we want to do is create a basic react component in the root of our project called
// index.js
import React from 'react';
class ParentComponent extends React.Component {
render() {
return (
<div>
<h1> Hi I'm the Parent Div </h1>
</div>
);
}
The next thing we want to do is create a child component in a different file
//child.js
import React from 'react';
export default class ChildComponent extends React.Component {
render() {
return (
<div>
<h2> Hi I'm the Child Div </h2>
</div>
);
}
Lets import our child component into our parent component
// index.js
import React from 'react';
import Child from "./child";
class ParentComponent extends React.Component {
render() {
return (
<div>
<h1> Hi I'm the Parent Div </h1>
<Child />
</div>
);
}
Lets create a grandchild component as well:
//grandchild.js
import React from 'react';
export default class GrandchildComponent extends React.Component {
render() {
return (
<div>
<h3> Hi I'm the GrandChild Div </h3>
</div>
);
}
Lets repeat the process we did for our parent and import our grandchild component into our child component
//child.js
import React from 'react';
import Grandchild from "./grandchild";
export default class ChildComponent extends React.Component {
render() {
return (
<div>
<h2> Hi I'm the Child Div </h2>
<Grandchild />
</div>
);
}
Now we’re done building our React components, but now we want to pass data into our Grandchild component without it going to our Child component.
Two concepts React uses in its Context api are the Provider and the Consumer.
The Provider is the highest level component in the Context api and contains the values that we want to pass down.
The Consumer component lets you access values from our higher order Provider component.
Let’s create a new file called context.js. We’re putting it into a separate file because want our Parent component and Grandchild component to be able to access this component.
//context.js
import React from "react";
const Context = React.createContext();
export default Context;
Next we want to add the provider to our parent component, this will allow us to use our consumer in any of our components
Lets import our context component into our function
// index.js
import React from "react";
import Child from "./child";
import Context from "./context";
class ParentComponent extends React.Component {
render() {
return (
<Context.Provider value={{ lesson: 'context for classes' }}>
<div>
<h1> Hi I'm the Parent Div </h1>
<Child />
</div>
</Context.Provider>
);
}
}
the value prop in our Context.Provider component will be the value we can pass into all of our parents descendant components.
The next step is to wrap the our Grandchild component with a Consumer component
//grandchild.js
import React from "react";
import Context from "./context";
export default class GrandchildComponent extends React.Component {
render() {
return (
<Context.Consumer>
<div>
<h3> Hi I'm the GrandChild Div </h3>
</div>
</Context.Consumer>
);
}
}
We’re not done yet, our context consumer must be wrapped over a function, and this function will contain a parameter containing all the attributes in the value prop we passed into our Context.Provider component in our parent function.
The function will return the content we originally had in our Grandchild component.
//grandchild.js
import React from "react";
import Context from "./context";
export default class GrandchildComponent extends React.Component {
render() {
return (
<Context.Consumer>
{( value ) => (
<div>
<h3> Hi I'm the GrandChild Div </h3>
</div>
)}
</Context.Consumer>
);
}
}
We can also access our lesson attribute from our value parameter.
//grandchild.js
import React from "react";
import Context from "./context";
export default class GrandchildComponent extends React.Component {
render() {
return (
<Context.Consumer>
{( value ) => (
<div>
<h3> Hi I'm the GrandChild Div </h3>
{value.lesson}
</div>
)}
</Context.Consumer>
);
}
}
We were able to pass in data originating from our parent component into our grandchild component without drilling our data down into our child component.