Next.js useContext Examples, Advanced Usage with useReducer Tutorial

React Context API is a powerful feature which enables to management and shares state information across various sections of the Next js application. Using Context API we can share data without passing the props down manually at every level for same. In this guide, we will discuss how to set up and use Context API…

By.

min read

React Context API is a powerful feature which enables to management and shares state information across various sections of the Next js application. Using Context API we can share data without passing the props down manually at every level for same.

In this guide, we will discuss how to set up and use Context API with various use cases using practical examples in the Next js application.

What is the React Context API?

In React js, the Context API is a state management tool which comes inside the library bundle. It helps to create a way to manually pass data through the component tree via props. It provides an efficient way to make it possible to share data or manage states between various React or Next js components in large applications.

 

Setting Up the Context API in Next.js

We will follow these steps and discuss how to easily implement it in Next js application:

Step 1: Create a Context File
Step 2: Wrap the Application with the Context Provider
Step 3: Use the Context in Your Components

 

Step 1: Create a Context File

To set up the Context API, first, we will create a context.js file in our Next js project’s context folder. So that the path of the file will be ~context/context.js.

This file will contain the context and the state that we can share across our application:

import { createContext, useState } from "react";

export const MessageContext = createContext(null);

function MessageProvider({ children }) {
  const [message, setMessage] = useState();

  return (
    <MessageContext.Provider value={{ message, setMessage }}>
      {children}
    </MessageContext.Provider>
  );
}

export default MessageProvider;

We created a context called MessageContext and a context provider MessageProviderwhich uses the useState hook to create a state variable message and its setter function named setMessage. Then these are passed as values to the MessageContext.Provider.

 

Step 2: Wrap the Application with the Context Provider

Thereafter, we need to wrap the application without a context provider. Open the _app.js file which is the main component in a Next js application then make following changes:

import MessageProvider from "../context/MessageContext";

function MyApp({ Component, pageProps }) {
  return (
    <MessageProvider>
      <Component {...pageProps} />
    </MessageProvider>
  );
}

export default MyApp;

 

Step 3: Use the Context in Your Components

Finally, we can use the useContext hook to access the context in various other components. Here is an example:

import { useContext } from "react";
import { MessageContext } from "../context/MessageContext";

function MyComponent() {
  const { message, setMessage } = useContext(MessageContext);

  // You can now use `message` and `setMessage` in your component
}

In above example, we have import the useContext hook and the MessageContext from our context.js file. We are then calling useContext with MessageContext as an argument to access the message state and the setMessage function.

 

Advanced Usage of Context API in Next.js

After discussing some of the basic implementations of Context API, let’s have a look on further at advanced use-cased and how it can help us in intensive and large applications:

 

1. Combining Multiple Contexts

In large applications, we can have multiple contexts to manage states related to different information for example you might have one context for user datatheme settings and another for application settings.

In that case, this is how you can combine multiple contexts:

 

Following is a sample context named User Context in file ~context/UserContext.js:

import { createContext, useState } from "react";

export const UserContext = createContext(null);

function UserProvider({ children }) {
  const [user, setUser] = useState(null);

  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
}

export default UserProvider;

 

Similarly, we have created another context to keep the theme-related context in file ~context/ThemeContext.js:

import { createContext, useState } from "react";

export const ThemeContext = createContext(null);

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState("light");

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

export default ThemeProvider;

 

Let’s have a look , at how to use and compile both above contexts in the ~pages/_app.js:

import UserProvider from "../context/UserContext";
import ThemeProvider from "../context/ThemeContext";

function MyApp({ Component, pageProps }) {
  return (
    <UserProvider>
      <ThemeProvider>
        <Component {...pageProps} />
      </ThemeProvider>
    </UserProvider>
  );
}

export default MyApp;

In this setup, both the UserContext and ThemeContext are available to all the components in the application.

 

2. Using Context with useReducer

To manage more complex state logic, the useReducer hook proves better than useState hook which we used earlier.

The useReducer allows to handle multiple states in a more organised way. Let’s have a look at how we can use useReducer in the context file ~context/AppContext.js:

import { createContext, useReducer } from "react";

export const AppContext = createContext(null);

const initialState = {
  count: 0,
};

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function AppProvider({ children }) {
  const [state, dispatch] = useReducer(reducer, initialState);

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

export default AppProvider;

In the above example, we used useReducer to manage a count state and dispatch function is used to update the state based on the action type.

 

3. Using createContext with TypeScript

When we use createContext with TypeScript, we can provide a type for the context value which will allow TypeScript to type-check the value passed to the Provider and the value returned from useContext.

Here is how to do it:

import React, { createContext, useContext } from 'react';

interface ContextType {
  message: string;
  setMessage: (message: string) => void;
}

const MessageContext = createContext<ContextType | undefined>(undefined);

const MessageProvider: React.FC = ({ children }) => {
  const [message, setMessage] = useState("Hello, world!");

  return (
    <MessageContext.Provider value={{ message, setMessage }}>
      {children}
    </MessageContext.Provider>
  );
};

function useMessageContext() {
  const context = useContext(MessageContext);
  if (!context) {
    throw new Error("useMessageContext must be used within a MessageProvider");
  }
  return context;
}

In the above typescript example, we defined an ContextType interface which describes the type of the context value.

 

Conclusion

The Context API is a useful and powerful state management tool for the Next js application. Context API allows us to pass the data efficiently without any dependency on passing the data as props manually.

We also discussed how to easily setup Context API in the Next js application and also discussed advanced use cases also how we can deploy useReducer hook instead of useState.

Make sure you pick the best use-case based on the architecture of your application. Using the simple context API also suffices the requirement but for advanced and large-scale applications we can user useReducer hook if required. Hope this will be helpful.

Leave a Reply

Your email address will not be published. Required fields are marked *