Jan 14, 2019

What is Context API in React? Why and When to use it?


Introduction:

This article explains about Context API in React.

What?

The definition for the Context API according to the documentation is “Context provides a way to pass data through the component tree without having to pass props down manually at every level”.

In simple words, the Context is the universal data for your application. The Context API provides the solution for sharing the state between components in the component tree. Instead of passing properties through intermediary components that do not necessarily use the props (known as the "prop drilling"), you can wrap your components in a Context.

Why?

Let us take a step back and understand the scenarios/problems we end up with if we didn’t have the concept Context(Before Version 16.3).

In the following example, we have a Parent component and 2 levels of child components.

Component hierarchy

In a traditional way, if you want to use the Parent state in the GrandChild1 then you should pass the state to its child component as props. Let's take a look at the code below.

Code Sample without Context:

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      grandChild1Age: 10,
      grandChild2Age: 8
    };
  }
  render() {
    return (
      <div>
        <Child1
          rootName={this.props.name}
          grandChild1Age={this.state.grandChild1Age}
          grandChild2Age={this.state.grandChild2Age}
        />
        <Child2 />
      </div>
    );
  }
}

class Child1 extends React.component {
  render() {
    return (
      <div>
        <GrandChild1 age={props.grandChild1Age} rootName={props.rootName} />
        <GrandChild2 age={props.grandChild2Age} />
      </div>
    );
  }
}

class GrandChild1 extends React.Component {
  render() {
    return (
      <div>
        I am GrandChild1 and {props.age} old. My root name is {props.rootName}
      </div>
    );
  }
}

class GrandChild2 extends React.Component {
  render() {
    return (
      <div>
        I am GrandChild2 and {props.age} old. My root name is {props.rootName}
      </div>
    );
  }
}

Problems with the above Code:

  • Code Maintainability is hard.
  • The code will NOT be DRY without contexts and that explains the reason!
  • The passing of data via props explicitly across multiple levels of components is referred to Props-drilling, and becomes a nightmare while debugging!

Code Sample with Context:

const contextDemo = React.createContext();

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      grandChild1Age: 10,
      grandChild2Age: 8,
      rootName: props.name
    };
  }

  render() {
    return (
      <div>
        <contextDemo.Provider value={this.state}>
          <Child1 />
        </contextDemo.Provider>
      </div>
    );
  }
}

class Child1 extends React.Component {
  render() {
    return (
      <div>
        <GrandChild1 />
        <GrandChild2 />
      </div>
    );
  }
}

class GrandChild1 extends React.Component {
  render() {
    return (
      <contextDemo.Consumer>
        {context => (
          <div>
            I am GrandChild1 and {context.grandChild1Age} old. My root name is{" "}
            {context.rootName}
          </div>
        )}
      </contextDemo.Consumer>
    );
  }
}

class GrandChild2 extends React.Component {
  render() {
    return (
      <contextDemo.Consumer>
        {context => (
          <div>
            I am GrandChild and {context.grandChild2Age} old. My root name is{" "}
            {context.rootName}
          </div>
        )}
      </contextDemo.Consumer>
    );
  }
}

ReactDOM.render(<Parent name="AK" />, document.getElementById("root"));

Demystifying the above code snippet:

  • React.createContext gives you both a Consumer and a Provider, that acts as a portal to put data into the Provider and it comes out in the Consumer.
  • The provider is used to push data into context. In Parent Component, we pushed its state into Context.
  • Even though we are not passing data to Child1 from Parent and GrandChild’s from Child1 we are able to read the data from GrandChild components.
  • The consumer is used to read data from the Context in GrandChild components.

When to use Context API:

  • Use context when you are using parent components data in more than 3-4 places down in the component tree and you could totally avoid it if your component hierarchy is not complex.
  • To avoid unnecessary props drilling
  • When your child components want to update the state of the parent, contexts can be used(event handler can be used in contexts.)
  • As a replacement for other state management tools for small application.
  • Easy to learn and implement compared to Redux, Flux and, other state management tools for React.
  • Even for a large application, You can use Context but other state management tools are out there from a long time and are more stable!

Abhilash Reddy Kotte

Frontend Engineering