How to use React context
Tags: react, javascript, webdev
Published: 2020-10-23 12:47:26

React Context is a powerful feature [introduced in React v16.3.0](https://reactjs.org/blog/2018/03/29/react-v-16-3.html). It allows you to access values from the current context (e.g. the parent) inside your components' render methods, where you normally would be limited to accessing only its local state and props . This creates a very flexible system for sharing values between components. In this article, we'll cover how to use React Context to create a shared state, and how to consume that state in a child component.

This article assumes you are familiar with the basics of React, such as JSX, components, state, and props . If you're not familiar with these concepts yet, I would recommend reading the [official React docs](https://reactjs.org/docs/getting-started.html) first.

## What is React Context?

React Context is a new feature that allows you to share data between components. It's similar in concept to React's `useState()` hook, but it allows you to share data across multiple components at once. The most common use case for using the context API is sharing state across one or more child components. Let's look at an example:

```jsx
import React from 'react'

const ParentComponent = () => {
    const [counter, setCounter] = React.useState(0)

    return (
        <ChildComponent
            counter={counter}
            onIncrement={() => setCounter(counter + 1)}
        />
    )
}

const ChildComponent = ({ counter, onIncrement }) => {
    return (
        <div>
            {counter}
            <button onClick={onIncrement}>increment</button>
        </div>
    )
}
```

Here, we have a `<ParentComponent>` that sets its own local state with an initial value of `0`. The component is rendering a `<ChildComponent />`, which is receiving the current `counter` state as prop. It also updates the parent's counter state when the increment button is clicked.

This works great for simple cases, but it can quickly get out of hand if we want to share more state between components. Components start to become more and more coupled, which makes them harder to maintain and reason about. We can do better than this!

Instead of passing the `counter` prop into `ChildComponent`, we can use the React Context API to share the counter state between both components. Here's how:

```jsx
import React from 'react'

const CounterContext = React.createContext(0)

const ParentComponent = () => {
    const [counter, setCounter] = React.useState(0)

    const increment = () => setCounter(counter + 1)

    return (
        <CounterContext.Provider value={{ counter, increment }}>
            <ChildComponent />
        </CounterContext.Provider>
    )
}

const ChildComponent = () => {
    const { counter, increment } = React.useContext(CounterContext)

    return (
        <div>
            {counter}
            <button onClick={() => increment()}>increment</button>
        </div>
    )
}
```

The `ChildComponent` is now consuming the counter state and increment action from the context instead of receiving them as props . This allows us to avoid coupling `ChildComponent` to `ParentComponent`.

## How is this different from `useState()`?

The biggest difference between the context API and `useState()` is that the context API allows you to share state between multiple components at once. The `useState()` hook is scoped to a single component only.

Another difference is that the context API can be used to share any type of data, not just state. Like functions for example. We can even use context to share an entire Redux store (but don't do that please).

## How does the context API work?

The `Context` object is a special type of React object that can be used to create and share data. It has two component: `Provider` and `Consumer`. The provider is used to create new context objects, while the consumer is used to access values from those contexts.

The context API allows you to provide multiple consumers with a single provider. For example:

```jsx
import React from 'react'

const CounterContext = React.createContext(0)

const ParentComponent = () => {
    const [counter, setCounter] = React.useState(0)

    const increment = () => setCounter(counter + 1)

    return (
        <CounterContext.Provider value={{ counter, increment }}>
            <DisplayCounterComponent />
            <IncrementCounterComponent />
        </CounterContext.Provider>
    )
}

const DisplayCounterComponent = () => {
    const { increment } = React.useContext(CounterContext)

    return <div>{counter}</div>
}

const IncrementCounterComponent = () => {
    const { increment } = React.useContext(CounterContext)

    return (
        <div>
            <button onClick={() => increment()}>increment</button>
        </div>
    )
}
```

Here, we are using the `CounterContext` provider to share the context between multiple components. The `DisplayCounterComponent` is retrieving the `counter` state and displays it value, while `IncrementCounterComponent` is retrieving the `increment` action from the context instead.

## Create a custom context provider

In the previous examples, we were using the `CounterContext.Provider` directly. But we can create our own `CounterProvider` component that will make it easier to reuse the context logic.

```jsx
import React from 'react'

const CounterContext = React.createContext(0)

const CounterProvider = ({ children, initialCount = 0 }) => {
    const [counter, setCounter] = React.useState(initialCount)

    const increment = () => setCounter(counter + 1)

    return (
        <CounterContext.Provider value={{ counter, increment }}>
            {children}
        </CounterContext.Provider>
    )
}
```

The `CounterProvider` component receives an initial count from the parent, which it uses to create the context's initial state. It also receives a `children` prop, which we are passing through so any child component will be rendered.

Now let's see how we can use `CounterProvider` in our app.

```jsx
import React from 'react'

const ParentComponent = () => {
    return (
        <CounterProvider initialCount={0}>
            <ChildComponent />
        </CounterProvider>
    )
}

const ChildComponent = () => {
    const { counter, increment } = React.useContext(CounterContext)

    return (
        <div>
            {counter}
            <button onClick={() => increment()}>increment</button>
        </div>
    )
}
```

The `useContext` hook is still used to retrieve our counter context within any child component. But we can create a custom `useCounterContext` hook for this for better reusability.

```jsx
const useCounterContext = () => React.useContext(CounterContext)
```

Now we can use `useCounterContext` instead of `useContext` inside our components.

```jsx
const ChildComponent = () => {
    const { counter, increment } = useCounterContext()

    return (
        <div>
            {counter}
            <button onClick={() => increment()}>increment</button>
        </div>
    )
}
```

## Closing thoughts

The context API is a powerful tool for creating shared state between multiple components. It's a great way of avoiding coupling and your components together, which makes them easier to test and maintain.

The [React docs](https://reactjs.org/docs/context.html) have a great article on the context API as well. It's worth taking a look at if you are interested in learning more.