Shared State with React Hooks and Context API
Tags: react
Published: 2022-02-01 13:57:31

---
title: Shared State with React Hooks and Context API
published: true
date: 2020-08-05 21:23:21 UTC
tags: react
cover_image: https://cdn.hashnode.com/res/hashnode/image/upload/v1596662576523/EH5DzPXUw.png
canonical_url: https://sabinadams.hashnode.dev/shared-state-with-react-hooks-and-context-api
---

With React 16.8 came the release of Hooks, which, along with other great functions, provides a way for function components to have a state!

> In my article [Managing State with React Hooks](https://blog.sabinthedev.com/managing-state-with-react-hooks-ckdggq1hc00d2dns166695i2w) I go over how to use the hooks `useState` and `useReducer` to manage state within function components. Give this a read if you need a refresher on how hooks can help manage state.

In this article we’ll take a look at a way to use hooks along with React’s `Context API` to provide a shared state between function components!

* * *

## Create and Provide a Context

While the Context API itself is not a part of hooks, using it along with the `useReducer` and `useContext` hooks, we can provide a reducer and state globally (or to specific component trees, depending on how you implement it) in our application.

> Not familiar with React’s Context API? Check out their [documentation](https://reactjs.org/docs/context.html#consuming-multiple-contexts) explaining what a Context is and what it can accomplish.

Let’s look at a simple example of how this would work!

First off, we’ll create a Context that will eventually provide the `state` and `dispatch function` of a reducer to the entire application. That way the entire application will be provided a way to access the reducer and state.

```
import React, { createContext, useReducer } from 'react'

export const StateContext = createContext()

```

### Build a component that provides the Context value to its children

Okay, so we have a Context created that will eventually hold the `state` and `dispatch`. How do we provide this to the entire application? We’ll need to wrap the entire application in the `Provider` we received from the Context. Let’s create a wrapper we can stick our app into that will provide the context value (the state and dispatcher) to its children.

```
import React, { createContext, useReducer } from 'react'

export const StateContext = createContext()

// This is a component that wraps all of its children in the Provider
export const StateProvider = ({ reducer, initialState, children }) => (
  <StateContext.Provider value={useReducer(reducer, initialState)}>
    { children }
  </StateContext.Provider>
)

```

The code above is doing a few things:

- It exports a new component named `StateProvider` that will take in a reducer, an initial state, and children as props. The `StateProvider` component wraps all of its children in the `Provider` we grabbed from the StateContext we created.
- We pass the reducer and initial state from this component’s props to the `useReducer` hook.
- The Provider accepts a prop named `value`. This value is the data that is provided to all of the `children`. The `useReducer` hook returns an array with two items: the current state, and a dispatch function. These are the values we are passing into the `value` prop, thus providing those to all of `StateProvider`‘s child components.

### Wrap the whole App in the `StateProvider` component

Awesome! We now have a component that will wrap all the children in a Provider that supplies them with the `state` and `dispatch` we require to share state across the entire application. Let’s wrap an app in this component so the children can consume the Context it provides.

```
import React from 'react';
import { StateProvider } from './State'

function StateApp() {
  // Initialize the state
  const initialState = {
    points: 0
  }

  // Defines how to update the state based on actions
  const reducer = (state, action) => {
    switch (action.type) {
        case 'add':
            return { ...state, points: state.points + 1 }
        default:
            return state;
    }
  };

  return (
    <StateProvider initialState={initialState} reducer={reducer}>
        // Application
    </StateProvider>
  )
}

export default StateApp

```

Here we create an initial state and a reducer to manage that state. We then wrap the app in our `StateProvider` component, passing in the `initialState` and `reducer`. As we saw above, `StateProvider` uses those to provide all of the children with `state` and `dispatch`. The result is global access to state!

* * *

## Consume the Context in a Child Component

Now let’s see how a child component would access and update the global state!

> In the past, if you wanted to access a Context you would need to wrap your component in that Context’s `Consumer`. Using the new `useContext` hook allows us to access the context without using the Consumer.

```
import React, { useContext } from 'react'
import { StateContext } from '../State'

function Points() {
  // Here, we get the state and dispatch from the context
  const [{ points }, dispatch] = useContext( StateContext )

  return (
    <>
        {/* Clicking this button runs the global dispatch we got from the Context */}
        <button onClick={() => dispatch({type: 'add'})}>Add</button>
        <p>{ points }</p>
    </>
  );
}

export default Points

```

In order to access the global state we need to import the `useContext` hook and the `StateContext` we created.

We use the `useContext` hook to get access to the data provided by a context. This hook takes one argument, a Context, and returns the current context value. In our case, the context value is the return value of our `useReducer` function which contains an array of two items. The first being the current state and the second a dispatch function.

> **NOTE** : I am using Destructuring to pull out only the `points` key from the state and to pull the items out of the array.

When the Add button is clicked, the global `dispatch` provided by the Context is fired, resulting in an increment of the global state’s `points` key value.

Neat! This component is now consuming and updating a global state that can be shared with other components within the `StateProvider` component’s component tree.

* * *

## Putting it all together

Here is a full example of all of this at work. Below is the `App.js` file which will wrap its children in the `StateProvider`.

```
import React from 'react';
import { StateProvider } from './contexts/State'
import GlobalStateCounter from './components/GlobalStateCounter'
import './App.scss'

function App() {
  // Initialize the state
  const initialState = {
    points: 0
  }

  // Defines how to update the state based on actions
  const reducer = (state, action) => {
    switch (action.type) {
        case 'add':
            return { ...state, points: state.points + 1 }
        case 'subtract':
            return { ...state, points: state.points - 1 }
        case 'reset':
            return { ...state, points: 0 }
        default:
            return state;
    }
  };

  return (
    <div className="App">
      <StateProvider initialState={initialState} reducer={reducer}>
          {
              // Creates two instances of the component so we can see both update when state changes
              [...Array(2)].map((e, i) => <GlobalStateCounter index={i + 1}/> )
          }
      </StateProvider>
    </div>
  )
}

export default App

```

And here is the child component that uses the `useContext` hook to access the `StateContext`‘s value, which is an array containing the `state` and a `dispatch` function.

```
import React, { useContext } from 'react'
import { StateContext } from '../State'
import '../assets/scss/Points.scss'

function Points({index}) {

  const [{ points }, dispatch] = useContext( StateContext )

  return (
    <div id="Points">
      <p className="title">Points (globalState {index})</p>
      <hr className="divider"/>
      <div className="pointsContainer">
          <div className="buttons">
            {/* These buttons use the dispatch to update the state */}
            <button className="button add" onClick={() => dispatch({type: 'add'})}>Add</button>
            <button className="button subtract" onClick={() => dispatch({type: 'subtract'})}>Subtract</button>
            <button className="button reset" onClick={() => dispatch({type: 'reset'})}>Reset</button>
          </div>
          <div className="outputBox">
            {/* Output the points variable */}
            <p>{ points }</p>
          </div>
      </div>
    </div>
  );
}

export default Points

```

* * *

![globalStateGif-1.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1596662402526/pk-Ek1gX9.gif)

Using React’s Context API and hooks we have implemented global state management! This is a basic example, but a strong foundation for a more complex and organized state management system.

* * *

## Conclusion

Using this structure, you can provide access to a global state across your entire application! This can act as a replacement for other forms of state management like `Redux`. More complexity can be added to this to make it more versatile and organized, but the general idea provides a great base to start managing shared states in your function components!

Thanks for reading!