How to use the React Context API?
Tags: react, javascript
Published: 2022-05-19 10:28:16

Okay, React Context API is among us for a while now, since **version 16.3** of React. But should we use it? And, most importantly, how can we use React Context API properly and what kind of problem we can solve with it?

In this discussion I am assuming the reader has a simple knowledge about React and React hooks API.

## The problem

In order to show the usual problem to be solved with React Context API, I created a very simple React App. I just used the boilerplate of [**create react app**](https://create-react-app.dev/docs/getting-started/)

```javascript
npx create-react-app react-context
```
and, once the application was ready I created the folder **components** inside **src** and inside of it I added the folders **App**, **ProductsList** and **ProductItem** as shown in the following image.

![initial files tree](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1aft8wi4jt8xklvk1gjz.png) 

Here is the code of each file.

### App/index.jsx
```javascript
import React, { useState } from 'react';

import ProductsList from '../ProductsList';

const App = () =>  {

  const initialProducts = [
    { id: 1, name: 'Apple', price: 0.45 },
    { id: 2, name: 'Onion', price: 0.54 },
    { id: 3, name: 'Meat', price: 3.55 },
    { id: 4, name: 'Milk', price: 0.86 },
    { id: 5, name: 'Bread', price: 1.18 },
  ];

  const [products, setProducts] = useState(initialProducts);

  const handleDeleteProduct = (product) => {
    const productElements = products.filter(prod => prod.id !== product.id);
    setProducts(productElements);
  };

  return (
    <ProductsList products={products} deleteProduct={handleDeleteProduct} />
  );

}

export default App;
```

### ProductsList/index.jsx

```javascript
import React from 'react';

import ProductItem from '../ProductItem';

const ProductsList = ({ products, deleteProduct }) => {
  return (
    <div style={{ marginLeft: '10px' }}>
      <h3>Products List</h3>
      <div>
        {products.map((product) => (
          <ProductItem 
            key={product.id}
            product={product}
            deleteProduct={deleteProduct}
          />
        ))}
      </div>
    </div>
  );
};

export default ProductsList;
```

### ProductItem/index.jsx
```javascript
import React from 'react';

const ProductItem = ({ product, deleteProduct }) => {
  return (
    <div style={{ 
      backgroundColor: '#646FD4',
      borderRadius: '5px',
      width: '120px',
      marginBottom: '10px' 
    }}>
      <div style={{ 
        textAlign: 'center',
        fontSize: '18px',
        fontWeight: 'bold',
        color: '#F5F5F5'
      }}>
        {product.name}
      </div>
      <div style={{
        textAlign: 'center',
        fontSize: '14px',
        fontWeight: 'bold',
        color: '#F5F5F5'
      }}>
        {product.price.toLocaleString("en-IE", {style:"currency", currency:"EUR"})}
      </div>
      <div style={{ 
        marginTop: '5px',
        textAlign: 'center',
      }}>
        <button 
          style={{
            backgroundColor: '#F32424',
            border: '1px solid #F32424',
            borderRadius: '5px',
            padding: '6px 8px',
            color: '#FFFFFF',
            fontWeight: 'bold',
            marginBottom: '5px',
          }}
          onClick={() => deleteProduct(product)}
        >
          Apagar
        </button>
      </div>
    </div>
  );
};

export default ProductItem;
```
Important thing here: I **do not** use css styles inline as shown in the code above. I'm just passing it inline because I wanted a very simple style instead of show you all the raw html. So, sorry for that! I hope you understand me.

Well, basically, the **App component** has an array of products as its state and a function that delete from that array some specific product. It only renders the **ProductList component** that receives both the array of products and the delete function as props.

The **ProductsList component**, as mentioned above receives the array of products and uses the map method to render to each product of the array one instance of the **ProductItem component**.

The **ProductItem component** just receives the product and the deleteProduct function as props and renders a simple div, showing the name and price of product and a button that, once clicked, deletes that product.

After all that effort, let's see what we have just created

![Application preview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/59r6vxe8v0dl4wzow3xq.gif)

Okay! Pretty good! Now, let's understand the hierarchy tree of our application so far.

![Components hierar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zmno98e5xlznynuuabcl.png)

It's just a simple hierarchy tree but it's enough to understand what is happening: despite the function **handleDeleteProduct** is created in the App component, since it need the use the products array to filter the element to be deleted, it will only be used in the **ProductItem component** and, even though, the **ProductsList component** needs to receive it by the **App component** to pass it, again as props to its son, the **ProductItem component**. In this situation, the **ProductsList component** is just a bridge where the prop needs to pass so it can be delivered into the component that will really use it.

This is called **prop drilling**, which as the name suggests, is basically a situation when the same data is being sent at almost every level due to requirements in the final level. Every React developer will face this situation eventually. You could tell me, come on, this is not a problem at all to pass it through the **ProductList component** but believe me, in a real world application, with a huge hierarchy tree, you can face the problem of one parent component passing props to a child component after several nodes in different directions. And yes, this can become a hell. More than that, in a real world application, we usually deal with other libraries in our projects and we need to use every single prop in those. For example, generally I use the **prop-types** library. Using this library, we need to set the type of each prop of the component and yes, it is very annoying to pass the same data to several components just because of some leaf component needs to use one element from the root or from some node above.

Okay, after this, kind of big explanation, how can we avoid the **prop drilling**? Well, let's think about functions in programming. You can have some variables defined locally or globally. Every single global variable can be used in any function as a parameter but none local variables can be used globally. So, by that logic, we should think about those data globally, right?

Right. Actually, roughly speaking, from the React point of view, our components play the same role as functions (remember the name function components?) in procedural programming and props are, exactly what you are thinking, local variables. And yes, React Context API will basically allow us to use some data globally in our components. That way, every single state in the context can be passed directly only to those components that really need to use it.

## The solution

Well, basically, the solution requires 3 steps:

1. Create the context;
2. Provide the context;
3. Consume the context;

Let's start with creating our context. In order to do that, I usually create a new folder inside src, called **context**, according to the following image.

![Files tree with context](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rq87cyftm7ew7gthhz0j.png)

Inside the context folder, we create a new file named **ProductsContext.jsx** with the following code

### ProductsContext.jsx

```javascript
import { createContext } from 'react';

const ProductsContext = createContext({});

export const ProductsProvider = ProductsContext.Provider;

export default ProductsContext;
```
We use the **createContext** function to (guess what?) create a the context and we store it in the variable **ProductsContext** and then, we use the **ProductsContext.Provider** to generate a wrapper from which we will deliver values to all components inside this wrapper. This provider is stored in the variable **ProductsProvider**.

Once this is done, we go back to the **App** component and import the provider.

```javascript
import { ProductsProvider } from '../../context/ProductsContext';
```
After that, we wrap up the everything in the return of the **App** component and we pass the *handleDeleteProduct* function as a method of the value object of the provider. Roughly speaking, the *handleDeleteProduct* is kind of becoming one global variable (in this case a function) that can be used to any component child of the **App** component. We also remove the props *deleteProduct* that was initially being passed to the **ProductsList** component.

### App/index.jsx

```javascript
import React, { useState } from 'react';

import { ProductsProvider } from '../../context/ProductsContext';

import ProductsList from '../ProductsList';

const App = () =>  { 

  const initialProducts = [
    { id: 1, name: 'Apple', price: 0.45 },
    { id: 2, name: 'Onion', price: 0.54 },
    { id: 3, name: 'Meat', price: 3.55 },
    { id: 4, name: 'Milk', price: 0.86 },
    { id: 5, name: 'Bread', price: 1.18 },
  ];

  const [products, setProducts] = useState(initialProducts);

  const handleDeleteProduct = (product) => {
    const productElements = products.filter(prod => prod.id !== product.id);
    setProducts(productElements);
  };

  return (
    <ProductsProvider value={{ deleteProduct: handleDeleteProduct }}>
      <ProductsList products={products} />
    </ProductsProvider>
  );

}

export default App;
```

Since we don't have to use the *handleDeleteProduct* inside the **ProductsList** component, we remove the *deleteProduct* as a prop of it and, of course, we do not have to pass it as props into the **ProductItem**

### ProductsList/index.jsx
```javascript
import React from 'react';

import ProductItem from '../ProductItem';

const ProductsList = ({ products }) => {
  return (
    <div style={{ marginLeft: '10px' }}>
      <h3>Products List</h3>
      <div>
        {products.map((product) => (
          <ProductItem 
            key={product.id}
            product={product}
          />
        ))}
      </div>
    </div>
  );
};

export default ProductsList;
```
Now, in the **ProductItem** component, we need to call the context in order to use the *deleteProduct* method to delete the product item. To do that, we make use of the *useContext* hook. So, we import the *useContext* from the React library and the **ProductContext** and just create a variable value to store the (again!) value of the context by making use of the *useContext* hook. To finish, we remove the *deleteProduct* as a received prop of **ProductItem** component and call the *value.deleteProduct* in the onClick of the delete button.

### ProductItem/index.jsx
```javascript
import React, { useContext } from 'react';

import ProductsContext from '../../context/ProductsContext';

const ProductItem = ({ product }) => {

  const value = useContext(ProductsContext);

  return (
    <div style={{ 
      backgroundColor: '#646FD4',
      borderRadius: '5px',
      width: '120px',
      marginBottom: '10px' 
    }}>
      <div style={{ 
        textAlign: 'center',
        fontSize: '18px',
        fontWeight: 'bold',
        color: '#F5F5F5'
      }}>
        {product.name} 
      </div>
      <div style={{
        textAlign: 'center',
        fontSize: '14px',
        fontWeight: 'bold',
        color: '#F5F5F5'
      }}>
        {product.price.toLocaleString("en-IE", {style:"currency", currency:"EUR"})}
      </div>
      <div style={{ 
        marginTop: '5px',
        textAlign: 'center',
      }}>
        <button 
          style={{
            backgroundColor: '#F32424',
            border: '1px solid #F32424',
            borderRadius: '5px',
            padding: '6px 8px',
            color: '#FFFFFF',
            fontWeight: 'bold',
            marginBottom: '5px',
          }}
          onClick={() => value.deleteProduct(product)}
        >
          Delete
        </button>
      </div>
    </div>
  );
};

export default ProductItem;
```

Well, that's it! We finish the task and we can use the *handleDeleteProduct* function defined in the **App** component in a non-direct of its child components. Again, this was just a simple application specifically created to show the **prop drilling** problem and how to solve it using the React context API. It can be tempting to say that we increase the difficulty of the example (which I definitely agree, considering only these components), but in a real world application, the React context API really increase your productivity.

An important observation to make here is that this context was very simple. But we can create contexts that passes through more than just a simple javascript function, but also entirely other components. For example, I use context to pass through alerts and modals components. This increases the productivity a lot, since every time I need to show an alert or a modal in any page, I just need to call the respective context as a function. I can also notice that many people is using contexts today as an alternative to Redux.