Context API In React Applications

Introduction

If you want to pass value from parent component to immediate child component, then we can use props (prop drilling). But if the child is 'N'th child of the parent (ex. 3,4,5, etc.) then prop drilling is not a good option. Because the middle of the components is not going to use those values and it is slowing down our application. How to resolve this problem? There is a solution called "Context API" in React. In this article, we are going to explore what is Context API and how to use it in React applications.

Context API

  • Context API is introduced in React 16.3.
  • The Context API is used to share the data with multiple components, without having to pass data through props manually.
  • The React Context API allows us to produce the global data and share it across the application. 
  • This is the alternative to "prop drilling" or moving props from grandparent to child/grandchild, and so on.
  • It is an easier, lighter approach than Redux for sharing the data across the application.
  • Use cases for Context API are Theme of the application, User language, Authentication values, etc. 
  • Context API consists of three major parts which are, 
    • Create Context - create the context that going to share across the application.
    • Provider - The component that provides the value.
    • Consumer - A component that is consuming the value.

Create Context

We need to create a context that can be shared across the application.

React.createContext() is used to create the context value.

//SampleContext.js
import React from 'react';
const SampleContext = React.createContext();
export default SampleContext;

Provider

  • It is the root component where the context values are going to share.
  • The Provider component accepts data (context value) to be passed to consuming components that are descendants of this Provider. 
  • One Provider can be connected to many consumer components.

The Provider can be created as follows,

<ContextComponent.Provider value={<value going to pass>}>
 ....
</ContextComponent.Provider>
//SampleProvider.js

import React from 'react';
import SampleContext from './SampleContext';

class SampleProvider extends React.Component {
  state = {
    logged_user_details: {
      name: 'Test Name',
      department: 'Test Department'
    }
  };

  render() {
    return (
      <SampleContext.Provider
        value={{ logged_user_details: this.state.logged_user_details }}>
        {this.props.children}
      </SampleContext.Provider>
    );
  }
}

export default SampleProvider;

In the above code, created the "SampleProvider" provider component and pass the logged user details which will be used across the application.

//App.js

import ChildComponent from "./components/ChildComponent";
import SampleProvider from "./SampleProvider";

function App() {
  return (
    <SampleProvider>
        <ChildComponent />
    </SampleProvider>
  );
}

export default App;

In the above code, to make the "SampleProvider" accessible to children or other components just wrapped the components with the "SampleProvider" component.

Created one more component "ChildComponent" to explain the functionality better. It is the middle component between Parent Component(SampleProvider) and GrandChildComponent which is not using any context values.

//ChildComponent.js

import React from 'react';
import GrandChildComponent from './GrandChildComponent';

const ChildComponent = () => {
  return (
    <>
      <h1> Child component </h1>
      <GrandChildComponent />
    </>
  );
};

export default ChildComponent;

Consumer

  • It is a component where we are going to consume or access the context values.
  • The way to accessing context values is by wrapping the component in Consumer. 

The Consumer can be created as follows,

<ContextComponent.Consumer>
    {context => (
      {/* access the context values */}
    )}
</ContextComponent.Consumer>

1) Consume context values without hooks in class component

//GrandChildComponent.js

import React from 'react';
import SampleContext from '../SampleContext';

class GrandChildComponent extends React.Component {
  render() {
    return (
      <SampleContext.Consumer>
        {context => (
          <>
            <h1> Grand Child component </h1>
            <h4>User Details</h4>
            Name : {context.logged_user_details.name}
            <br />
            Department : {context.logged_user_details.department}
          </>
        )}
      </SampleContext.Consumer>
    );
  }
}

export default GrandChildComponent;

In the above code, the "GrandChildComponent" component is consumer component. So just wrap with  "<SampleContext.Consumer>  </SampleContext.Consumer>" and access the context values inside it.

In this approach, whenever the values are getting changed in the provider component, automatically the changes will reflect in all consumer components.

2) Consume context values with hooks in the function component

Same above example can be implemented using useContext() hook as follows,

//GrandChildComponentHook.js

import React from 'react';
import SampleContext from '../SampleContext';

GrandChildComponentHook.js

const GrandChildComponentHook = () => {

  const context = React.useContext(SampleContext);

  return (
    <>
      <h1> Grand Child component Hook</h1>
      <h4>User Details</h4>
      Name : {context.logged_user_details.name}
      <br />
      Department : {context.logged_user_details.department}
    </>
  );
}

export default GrandChildComponentHook;

In the above code, we have used "useContext()" hooks for consuming the context values in the function component.

Update Context values

If you want to update context values from the child components then, just pass events/methods with context values and invoke that method from the consumer component to update the values.

1) Modify the Provider component as follows,

// SampleProvider.js

import React from 'react';
import SampleContext from './SampleContext';

class SampleProvider extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      logged_user_details: {
        name: 'Test Name',
        department: 'Test Department'
      }
    }

    this.setUserDeatils = this.setUserDeatils.bind(this);
  };

  // Method to update state
  setUserDeatils(userDeatils) {
    this.setState({ logged_user_details: userDeatils });
  }

  render() {
    return (
      <SampleContext.Provider
        value={{ logged_user_details: this.state.logged_user_details, setUserDeatils: this.setUserDeatils }}>
        {this.props.children}
      </SampleContext.Provider>
    );
  }
}

export default SampleProvider;

In the above code, added the setUserDeatils() method to update the user details and passed it in the context values.

2) Modify the Consumer component as follows,

// GrandChildComponent.js

import React from 'react';
import SampleContext from '../SampleContext';

class GrandChildComponent extends React.Component {

  click(setUserDeatils) {
    let logged_user_details = {
      name: 'New Test Name',
      department: 'New Test Department'
    };
    setUserDeatils(logged_user_details);
  }

  render() {
    return (
      <SampleContext.Consumer>
        {context => (
          <>
            <h1> Grand Child component </h1>
            <h4>User Details</h4>
            Name : {context.logged_user_details.name}
            <br />
            Department : {context.logged_user_details.department}
            <br />
            <br />
            <button onClick={() => this.click(context.setUserDeatils)}>Update</button>
          </>
        )}
      </SampleContext.Consumer>
    );
  }
}

export default GrandChildComponent;

In the above code, Once you clicked the "Update" button, then setUserDeatils() will invoke with new values. Then context will be updated with new values and it will reflect across the application.

Before Update

After Update

Summary

  • The Context API is used to share the data with multiple components, without having to pass data through props manually. 
  • It is an easier, lighter approach for sharing the data across the application. 
  • Context API can be implemented using,
     
    • Create Context 
    • Provider
    • Consumer

I hope you have liked it and know about context API and its benefits in React applications.