useContext hook and context API. What are they and how do you use them?
Tags: react, webdev
Published: 2020-07-12 20:20:44

<h1> Content Outline </h1>
<ul>
  <li> <a href = "#what-is-hook"> Introduction to hooks </a> </li>
  <li> <a href = "#prop-drilling"> Prop drilling </a></li>
<li> <a href = "#how"> How to use context API in class components </a>  
     <ul>
     <ol>
         <li> <a href = "#create-instance"> Create an instance of context </a></li>
<li> <a href = "#wrap"> Wrap children of parent component (provider of data) inside context.Provider </a></li>
<li> <a href = "#pass-data"> Pass data via value attribute of context.Provider component </a></li>
<li> <a href = "#access-data-passed"> Access data passed from context.Provider from any descendant of parent component (App) via context.Consumer </a></li>
     </ol>
</li>
 </ul>
<li> <a href = "#usecontext"> useContext hook </a></li>
<li> <a href = "#reference"> References </a></li>


</ul>




<h1 id = "what-is-hook">  What is a hook? </h1>

A hook is a special function which lets you use functional components without writing ES6 class components. To appreciate the usefulness of `useContext` hook, it is important to understand the problem it is trying to solve. In the sections below, we discuss how `context` API is used in both class and functional components.

<h1 id = "prop-drilling"> Prop drilling </h1>

In React, data can be passed from a component to its descendants primarily via props. It is not a big deal to pass props from a parent component to its descendants one or two levels deep, but it becomes a mess if components are deeply nested. This problem is illustrated in the code below.

```javascript
class App extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return <ChildComponent name={"Joe"} />;
  }
}

class ChildComponent extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return <GrandChildComponent name={this.props.name} />;
  }
}

class GrandChildComponent extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return <GreatGrandChildComponent name={this.props.name} />;
  }
}

class GreatGrandChildComponent extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return <h1> Hello {this.props.name} </h1>;
  }
}
```

You can see how props have to be passed through all intermediate components from `App` in order to get to `GreatGrandChildComponent`. This, according to [Kent C. Dodds](https://kentcdodds.com/), is called [prop drilling](https://kentcdodds.com/blog/prop-drilling). Prop drilling is undesirable if the intermediate components have no use for the data. Your goal is to have the data sent to a specific component within the component tree. `context` API was developed to address this problem. It helps in passing data from parent component to child components without drilling through successive descendants via props.

<h1 id = "how"> How to use context API in class components  </h1>
   
Inorder to use context API in class components, you go through the following steps
<ol>
  <li> Create an instance of context using React.createContext</li>
  <li> Wrap children components of provider (component which dispatches data ) inside context.Provider </li>
  <li> Pass data via value attribute of context.Provider component </li>
  <li> Access data passed from context.Provider from any descendant of parent component via context.Consumer </li>
</ol>

 <h2 id = "create-instance">Create an instance of context</h3>

In class components, you can use `createContext` to create an instance of `context`. `createContext` takes context as an argument and returns an instance of the context. Context is an object whose `value` property  holds the data you want to pass. You can also create an instance of context without passing context(data) to `createContext`. In that case you will pass data via value attribute of `context.Provider` as explained in the subsequent sections.

```javascript
import React from "react";
const myContext = React.createContext();
```

Alternatively, you can also import `createContext` instead of using `React.createContext()` like:

```javascript
import React, { createContext } from "react";
const myContext = createContext();
```

A call to `createContext` returns an object which in the case above is assigned to the variable `myContext`. You can give a meaningful name to instance of `createContext` instead of calling it `myContext` One of the properties of `myContext` object is `Provider` whose usage is explained below.

<h2 id = "wrap">  Wrap children of parent component (provider of data) inside context.Provider</h2>

`myContext` has a `Provider` property which can be used inside component which provides data by wrapping siblings of the parent component in it. To illustrate this, let us go back to the previous example. If you want to pass data from `App` component down the component tree to any of the descendant components, then `App` is the provider of the data and is a parent to the components below it in the component tree. Simply wrap children of `App` inside `context.Provider`. This is illustrated in the code below.

```javascript
import React from "react";
const myContext = React.createContext();
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { name: "John Doe" };
  }
  render() {
    return (
      <myContext.Provider value={{ ...this.state }}>
        <ChildComponent />
      </myContext.Provider>
    );
  }
}
```

<h2 id = "pass-data"> Pass data via value attribute of context.Provider component </h2>

`myContext.Provider` has an attribute called `value` which is used for passing data.
In the code above, notice how `React.createContext` has been invoked. Its invocation returns a context object which is stored in `myContext`. In the `render` method of the component (in this case `App`) which provides the data to be passed down the component tree , you wrap the child component in `myContext.Provider` and pass the data via `value` attribute of `myContext.Provider`. This way, any component which is a descendant of `App` can access the data as explained below.

<h2 id = "access-data-passed"> Access data passed from context.Provider from any descendant of parent component (App) via context.Consumer </h2>

In the sections above, we looked at how to create context and pass data down the component tree via `context.Provider`. Let us look at how descendants of `App` in the component tree can access the data passed. `myContext`, context object returned by `createContext` has a property called `Consumer` which you can use to access the data.  In the code below we assume `GrandChildComponent` wants to use data which we passed from `App`.

```javascript
class GrandChildComponent extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <myContext.Consumer>
        {context => <h1> {context.name} </h1>}
      </myContext.Consumer>
    );
  }
}
```

Notice the child of `myContext.Consumer` is a function. The function takes the `context` (in this case `myContext`) as argument and  returns JSX which renders the data passed. This can be done in any component which is a descendant of component from which data was passed. This helps to avoid prop drilling. Though `context` API solves problem of drilling components to pass data around, it introduces complexity in `myContext.Consumer` because a function has to be passed as its child. If you want to avoid this complexity, you can use `useContext`. It should be noted that `useContext` is a hook, therefore its use is restricted to functional components.


<h1 id = "usecontext"> useContext hook </h1>

In the sections above, we looked at how `context` API is used in class components. Dispatching `context` in both `class` and `functional` components is the same. The difference is in the way `context` is consumed. To use data passed from a `parent` component via `context`, you use `context.Consumer` component  in ES6 classes. A function is passed as a child of `context.Consumer`. The function takes `context` as an argument and returns  JSX which renders the data passed via context.

In functional components on the other hand, `useContext` hook takes a context object (object returned by `React.CreateContext`) as an argument and returns the object passed via `value` prop of `Context.Provider`. The current context value is determined by the `value` prop of the nearest `Context.Provider` in the component tree in case a component has multiple ancestors dispatching data via `Context.Provider`.

It should be noted that a component which renders data passed via `context` API will always re-render when the context value changes. The code below is the functional component equivalent of `context.Consumer`.

```javascript
   const myContext = React.useContext(context);

```
You can now access the data passed as `value` attribute of `context.Provider` using `myContext`.

___

Thanks for reading this article till the end. If you find it informative, you can share it on [Twitter](http://www.twitter.com/). Others might find it useful too. In case you notice anything technically inaccurate, leave a comment below.

<h1 id = "reference"> References </h1>

<ul>
<li> <a href = "https://reactjs.org/docs/hooks-reference.html"> React Hooks API reference</a> </li>
</ul>