Creating Context Wrapper component in React

How to create a Context Wrapper component in React to manage global state in a React application.

Creating Context Wrapper component in React

After the introduction of Hooks in React 16.8, managing global state in a React application has become much easier. One of my favorite and simple ways to manage global state in a React application is by using the Context API.

From react.dev documentation example, lets create a simple context and provider for switching themes in a React application.

// Context.js

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

const ThemeContext = createContext();

export ThemeContext
// App.js

import React, { useContext } from 'react';
import ThemeContext from './Context';

function App() {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <div>
        <button onClick={() => setTheme('light')}>Light Theme</button>
        <button onClick={() => setTheme('dark')}>Dark Theme</button>
      </div>
    </ThemeContext.Provider>
  );
}

This is a simple example of how you can use the Context API to manage global state in a React application. But as the application grows, the number of contexts and providers and the methods to update the state will also grow. To manage this, we can create a Context Wrapper component that will manage all the contexts and providers in a single component.

// ThemeProvider.js

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

const ThemeContext = createContext();

// Instead of exporting the context, we can export the provider
export const ThemeProvider = ({ children }) => {

  // All the state and methods to update the state can be defined here
  const [theme, setTheme] = useState('light');

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

// We can also create a custom hook to use the theme context
export const useTheme = () => {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
};

Now we can use the ThemeProvider component to wrap our application and use the useTheme hook to access the theme state.

// App.js
import React from 'react';
import { ThemeProvider, useTheme } from './ThemeProvider';
import ThemeSwitcher from './ThemeSwitcher';

function App() {
  return (
    <ThemeProvider>  { /* There are no props to pass to the ThemeProvider */ }
      <div>
        <ThemeSwitcher />
      </div>
    </ThemeProvider>
  );
}
// ThemeSwitcher.js
import React from 'react';
import { useTheme } from './ThemeProvider';

function ThemeSwitcher() {
  const { theme, setTheme } = useTheme(); // The useTheme hook is accessible here as we have wrapped the App component with the ThemeProvider

  return (
    <div>
      <button onClick={() => setTheme('light')}>Light Theme</button>
      <button onClick={() => setTheme('dark')}>Dark Theme</button>
    </div>
  );
}

Because we are treating the ThemeProvider as a wrapper component, we can add as many contexts and providers as we want in the ThemeProvider component and use the custom hooks to access the state and methods to update the state.

This is also a good pattern for managing global components like modals, toasts and global uploaders in a React application. They can be accessed from anywhere and can also render children components like upload progress through out the application.

Continue Reading

Browse All Articles