Demystifying React Hooks: useContext
Tags: react, javascript, beginners, webdev
Published: 2020-06-27 15:36:24

React Hooks changed the way functional components are used providing them with a simpler way to encapsulate stateful behavior and side effects in a user interface. Since some hooks are easier to understand and use than others, this series of posts will focus on demystifying the hooks that are not as straightforward. 

So far, we explored useCallback, useMemo, and useRef in depth. This post will start by exploring the difference in between ***prop drilling*** and ***context***, followed by defining a ***context object***, explaining ***how to use the useContext() hook***, and ***how to optimize its performance.***

## Prop drilling vs Context

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/fpgl2l61he7v9t8npp0b.png)

React provides us with a data-flow where a parent component uses props to share data with its children. This way of tracking data works great for small apps, however, as your application grows you might find yourself passing a prop through multiple layers of components. This is called ***prop drilling.***

When passing props through multiple layers, identifying where data is being initialized and when data is actually being used can become very challenging and cumbersome. In addition, refactoring your code could lead to passing unnecessary props or using multiple names for one prop (AKA bugs!). 

An alternative to ***prop drilling*** is to use ***Context***,  a simple and light solution that gives us  the capability of accessing data across components even when they don’t have a parent-child relationship.

## What is a context object?

A ***context object*** is created using the `createContext()` API and its composed by two elements:

**Provider:** it provides the value
**Consumer:** it consumes the value

To create a context object, you can initialize it empty or with a value:

```javascript
const testContext = createContext();
```

And you can access its elements by destructuring them in this way:

```javascript
const { Provider, Consumer } = testContext;
```

## How to use the Provider?

The `Provider` in your ***context object*** needs to wrap around the parent element of a component-tree. This gives every component under that component-tree access to your global data. Take a look at the `<Provider>` tags below, they are making the `name` state accessible to all the components being wrapped. Now, the components `<NameModifier />` and `<NamePrinter />` (and any of their children) have access to the state `name` even though we are not passing `name` as a prop.

```javascript
const App = () => {
  const { Provider } = testContext;
  const [name, setTestName] = useState(“Milu”);

  return (
    <Provider value={{ name }}>
      <NameModifier />
      <NamePrinter />
    </Provider>
  );
};
```

## How to access our global data using useContext()?

The ***useContext()*** hook accepts a ***context object*** (defined above) and returns the current values made available by the Provider as static variables. 

Here we have our `<NamePrinter />` component (wrapped by the Provider tag in the previous section of code) and we are accessing the value of `name` by using our `userContext()` hook.

```javascript
export const NamePrinter = () => {
    const { name }  = useContext(testContext);

    return <div>My name is {name}!</div>
};

```

>Did you notice how `<NamePrinter />` is not taking any props? The `name` value is being accessed using the ***useContext()*** hook instead.

## How do I update my context?

You can also make functions available through your ***Provider***! 

In the following example, I’ve created a function called `updateName()` that allows you to modify the `name` state. If you take a look at the `<NameModifier />` component, I’m accessing the `updateName()` function using the useContext hook and calling it every time my input changes.

{% codepen https://codepen.io/Milu5489/pen/vYLJRwR %}

## What about performance?

A component that uses ***useContext()*** will re-render when the value in the ***context object*** is updated. You could run into an instance where one of the values in your context changes very often, which could cause all your components using ***useContext()*** to re-render even though the fast-changing-value is only used in a small component-tree. 

**The recommended solution is to split Context.** Therefore if you have Light/Dark Themes and a toggle to choose in between them that most likely won’t change too often in comparison to other values being shared by your context, you want to create a `ThemeContext` and `AppContext` as shown below.

```javascript
const App = ({ user, theme, themeToggle }) => {

  return (
    <ThemeProvider value={{ theme, themeToggle }}>
      <AppContext value={{ user }}>
        <HomePage />
      </AppContext>
    </ThemeProvider>
  );
};
```

## Summary

The use of a ***context*** object is a great alternative to ***prop drilling***. It allows you to access global data without passing it as props and it subscribes to it to re-render when it changes.

The ***context*** object contains two elements: `Provider` and `Consumer`.

The `Provider` element needs to wrap the component-tree that will have access to the global data.

The ***useContext()*** hook allows you to access the global data from any child components in the component-tree under the `Provider` wrapper.

To avoid unnecessary re-renders, split your ***context***. i.e. using `ThemeContext` and `AppContext`.

----

I hope this explanation of ***useContext()*** was helpful and that you will be applying these new concepts in future applications!

I post new content every week. We will be exploring a different React hook next Weekend. Follow me on [Twitter](https://twitter.com/milu_franz) and [Dev.to](https://dev.to/milu_franz) to keep up with new posts!