What is Context?
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
So If we have a parent component with child components inside it and each child component has further child components, then to pass data from the parent component to the innermost child component we have to pass it through all the parent components of the innermost child component. This is known as prop drilling
.
This means even if any component is not using that prop, it has to receive that prop to pass it down to the child component. So to avoid this long chain of passing props through each level, we can use context API provided by React.
So using Context API, the innermost child can directly access the prop data sent from the parent component just like the connect
method of react-redux
library.
That’s enough for details. Let’s start with code to understand it better.
Clone the project from this repository which is created in this article.
To run the application, execute the following commands in sequence
- yarn install
- yarn start
If you check the JSX returned from src/components/HomePage.js
file, it looks like this:
If you see, the UsersList
component accepts users
and isLoading
props and the Filters
component accepts handleSort
and sortOrder
prop.
If we have child components inside Filters
or UsersList
component which also needs these props, we have to pass them down from these components. So instead of passing those as props, we can access them using Context API.
There are 2 ways we can access the props passed from the context in React. Let’s start with the first approach.
Using render props pattern
Create a new context
folder inside the src
folder and add UserContext.js
file inside it with the following code:
import React from 'react';
const UserContext = React.createContext();
export default UserContext;
Here, we are creating a UserContext
variable which we can use to access the data passed from the parent component
Now, in HomePage.js
, import the UserContext
we just created at the top:
import UserContext from '../context/UserContext';
The React.createContext()
function call returns an object with two properties, Consumer
and Provider
which we can access directly from UserContext
variable as UserContext.Consumer
and UserContext.Provider
or using destructuring as shown below:
const { Consumer, Provider } = UserContext;
So now, change the JSX returned in HomePage
component from this code:
<React.Fragment>
<Header handleSearch={handleSearch} />
<Filters handleSort={handleSort} sortOrder={sortOrder} />
<UsersList users={users} isLoading={isLoading} />
</React.Fragment>
to this code:
<UserContext.Provider value={{ users, sortOrder, isLoading }}>
<Header handleSearch={handleSearch} />
<Filters handleSort={handleSort} />
<UsersList />
</UserContext.Provider>
The Provider
component accepts props inside value prop where we can pass all the props that we need to access in the components mentioned in between the opening and closing tag.
So now Header
, Filters
and UsersList
component can directly access the props we have provided in value prop.
Now, to access the sortOrder
prop inside the Filters
component, we need to change the returned JSX in Filters.js
from this code:
<div className="sortBy">
Sort by age
<Select
value={sortOrder}
className="select-filter"
onChange={handleSort}
options={options}
/>
</div>
to this code:
<UserContext.Consumer>
{(props) => (
<div className="sortBy">
Sort by age
<Select
value={props.sortOrder}
className="select-filter"
onChange={handleSort}
options={options}
/>
</div>
)}
</UserContext.Consumer>
Here, In between the opening and closing tag of UserContext.Consumer
, we provide a function for which the props we passed from HomePage.js
will become automatically passed.
<UserContext.Provider value={{ users, sortOrder, isLoading }}>
So the props will contain all the props passed such as users
, sortOrder
and isLoading
. We need only sortOrder
prop in Filters.js
so we can use destructuring here to further simplify it as:
<UserContext.Consumer>
{({ sortOrder }) => (
<div className="sortBy">
Sort by age
<Select
value={sortOrder}
className="select-filter"
onChange={handleSort}
options={options}
/>
</div>
)}
</UserContext.Consumer>
Now, we will access the isLoading
and users
prop inside UsersList
component
Change UsersList
component from this code:
const UsersList = ({ users, isLoading }) => {
return (
<div className="users-list">
{isLoading ? (
<p className="loading">Loading...</p>
) : (
<React.Fragment>
{users.map((user, index) => (
<UserItem key={index} {...user} />
))}
</React.Fragment>
)}
</div>
);
};
to this code:
const UsersList = () => {
return (
<UserContext.Consumer>
{({ isLoading, users }) => (
<div className="users-list">
{isLoading ? (
<p className="loading">Loading...</p>
) : (
<React.Fragment>
{users.map((user, index) => (
<UserItem key={index} {...user} />
))}
</React.Fragment>
)}
</div>
)}
</UserContext.Consumer>
);
};
Note, we have removed the props passed in the component and we are accessing it inside the UserContext.Consumer
render prop.
So we are accessing the isLoading
and users
props passed from the Context provider which is directly passed to the function defined in between the UserContext.Consumer
.
The pattern of defining a function in between the opening and closing tag is known as the render props pattern
.
You can find complete source code until this point here.
Using useContext hook
The React hooks API provides an easy way of accessing the passed props from consumer so there is no need of using render props pattern where we create a function to get the props values.
Just to recap, we pass the value prop to UserContext.Provider
as:
<UserContext.Provider value={{ users, sortOrder, isLoading }}>
Here, we are passing the object to the value prop with users
, sortOrder
and isLoading
properties.
So we can access these object properties by using useContext
hook as below:
import { useContext } from 'react';
import UserContext from '../context/UserContext';
const { users, sortOrder, isLoading } = useContext(UserContext);
Here, we have provided the UserContext
we created in UserContext.js
to useContext
hook as a parameter.
So to access sortOrder
inside the Filtes.js
file, we can just use:
const { sortOrder } = useContext(UserContext);
and in UsersList.js
, we can access the users
and isLoading
prop values as
const { users, isLoading } = useContext(UserContext);
Note: You can use the
useContext
hook to access props only in functional components and not in class based components. If you are using class based components, you have to use the render props pattern.
You can find complete source code for useContext
hook functionality here.
Conclusion
React provides an easy way to avoid prop drilling using Context API. There are two ways to access the passed props. The first is using render props pattern
and the second is using useContext
hook.
Note that, we have just passed users
, sortOrder
and isLoading
in the value prop. If you want you can even pass the handleSort
and handleSearch
handlers in the value prop. There is no problem with doing that.
Also, If you have nested children like parent component contains child component and that child component contains another child component and the innermost child is using the state or props of parent component, then only you should use context otherwise you can just directly pass props.
Context just provides a better way to access props directly in the innermost child without the need of passing the props through each child to reach the innermost child.
You can find the initial source code here, code for context using render props here and code for context using useContext here.
Thanks for reading!
Want to learn all ES6+ features in detail including let and const, promises, various promise methods, array and object destructuring, arrow functions, async/await, import and export and a whole lot more from scratch?
Check out my Mastering Modern JavaScript book. This book covers all the pre-requisites for learning React and helps you to become better at JavaScript and React.
Check out free preview contents of the book here.
Also, you can check out my free Introduction to React Router course to learn React Router from scratch.
Want to stay up to date with regular content regarding JavaScript, React, Node.js? Follow me on LinkedIn.