What is Context API in React and How to use it in React App

What is Context API in React and How to use it in React App

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

  1. yarn install
  2. yarn start

If you check the JSX returned from src/components/HomePage.js file, it looks like this:

JSX

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.

Did you find this article valuable?

Support Yogesh Chavan by becoming a sponsor. Any amount is appreciated!