Managing Global State with useReducer and Context API in Next JS 14
Tags: nextjs, react, javascript, tutorial
Published: 2024-04-03 07:33:02

Salut,
I've been a big fan of Next JS since the beginning but since Next JS 13, many things changed compared to Next JS 12. One of the main differences is Redux and Global State. Managing a global state can be a challenging task, especially when the state needs to be shared across multiple components. One reason is that Redux is only available on the client side. The question is how can we manage the global state on the server side? I came up with the solution context API. However, if we have to manage complex logic, the combination of useReducer and the Context API provides an elegant solution to this problem. In this tutorial, we will create a simple counter application to demonstrate how to use useReducer and the Context API to manage the global state in React. Let's dive in to the example.

## Step 1: Setting Up the Context and Reducer
First, let's create a new context for our counter application and define a reducer function that will handle state updates:

```
"use client";
import React, { createContext, useContext, useReducer } from 'react';

// Create a new context for the counter
const CounterContext = createContext();

// Reducer function
const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
};

```

In this code, we define a new context called CounterContext using React's createContext function. We also define a reducer function that takes the current state and an action, and returns the new state based on the action type.


## Step 2: Creating the Counter Provider
Next, let's create a CounterProvider component that will use the useReducer hook to manage the state and provide the state and dispatch function to its children:

```
// Counter provider component
const CounterProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <CounterContext.Provider value={{ state, dispatch }}>
      {children}
    </CounterContext.Provider>
  );
};

```

In this code, we use the useReducer hook to create a state and dispatch function pair. We then use the CounterContext.Provider component to wrap the children components, providing them with the state and dispatch function via the context.


## Step 3: Consuming the Counter Context
Now, let's create a custom hook called useCounter that will allow us to consume the counter context in our components:

```
// Custom hook to consume the CounterContext
const useCounter = () => {
  const context = useContext(CounterContext);
  if (!context) {
    throw new Error('useCounter must be used within a CounterProvider');
  }
  return context;
};

```

In this code, we use React's useContext hook to access the counter context. We also perform a check to ensure that the hook is used within a CounterProvider component, throwing an error if it is not.


## Step 4: Creating Components Using the Counter Context
Finally, let's create two components, CounterDisplay and CounterControls, that will use the useCounter hook to interact with the counter state:

```
const CounterDisplay = () => {
  const { state } = useCounter();

  return <div>Count: {state.count}</div>;
};

const CounterControls = () => {
  const { dispatch } = useCounter();

  return (
    <div>
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
    </div>
  );
};

```

In these components, we use the useCounter hook to access the counter state and dispatch function. The CounterDisplay component displays the current count, while the CounterControls component provides buttons to increment and decrement the count.


## Step 5: Using the Counter Provider in the App
Finally, let's use the CounterProvider component in our App component to wrap the CounterDisplay and CounterControls components:

```
const HomePageWrapper = () => {
  return (
    <CounterProvider>
      <CounterDisplay />
      <CounterControls />
    </CounterProvider>
  );
};

```

In this code, we wrap the CounterDisplay and CounterControls components with the CounterProvider component to provide them with access to the counter state and dispatch function.


## Conclusion
In this tutorial, we have learned how to use useReducer and the Context API to manage the global state in a Next JS 14 application. By using these tools, we can create a centralized state management system that makes it easy to share state across multiple components.

I hope this will be helpful, love to connect with you on [LinkedIn](https://www.linkedin.com/in/muhammad-azfar-aslam)

__________________________________________________________________

**Few more articles**
- [Highlight on the new features of Next JS 14 config 2023](https://dev.to/muhammadazfaraslam/highlight-on-the-new-features-of-next-js-14-config-2023-4lbe)
- [Highlight on the new features of Next JS 13 config 2022](https://dev.to/muhammadazfaraslam/highlight-on-the-new-features-of-next-js-13-config-2022-1a3i)
- [How to configure React JS app on WSL2 Windows 10](https://dev.to/muhammadazfaraslam/how-to-configure-react-js-app-on-wsl2-windows-10-3d1n)
- [Custom 'Tooltip' component using Tailwind and React Js](https://dev.to/muhammadazfaraslam/custom-tooltip-component-using-tailwind-and-react-js-186g)
- [JavaScript and React Interview questions (Junior/Mid-level) 2023](https://dev.to/muhammadazfaraslam/javascript-and-react-interview-questions-juniormid-level-2023-hd1)