Using Context API with Custom Hooks

yellow Volkswagen van on road

Previously I shared a post about How Context API fits into Component-driven architecture and shared a small glance of using Context API with a custom hook. This post dives deep into that specific part of the previous entry, this way you can have a broader understanding of the flexibility React offers when using two of its most powerful tools available and ends up offering a powerful solution for state management that simplifies sharing data between components without the need for prop drilling. In this post, we’ll explore how to effectively use the Context API with custom hooks, discuss its benefits, and provide practical examples. Let’s start!

What is the Context API?

The Context API is a feature in React that allows you to share values (state, functions, etc.) across the component tree without having to pass props down manually at every level. This is particularly useful for global data like themes, user authentication, or any other state that needs to be accessible in many components.

Why Combine Context API with Custom Hooks?

  1. Encapsulation of Logic: Custom hooks can encapsulate the logic of consuming context, making your components cleaner and easier to read.
  2. Reusability: You can create a custom hook that utilizes the Context API and reuse it across different components, promoting DRY (Don’t Repeat Yourself) principles.
  3. Separation of Concerns: By separating the logic of accessing context from the component logic, you can enhance maintainability.

Creating a Context with a Custom Hook

Now the part that everyone was waiting for 😜 the code! Let’s see one of the most common use cases the context is being used for: light/dark theme selection. So, let’s start from the beginning.

Step 1: Create the Context

First, you’ll need to create a context for the data you want to share. For this example, let’s create a simple theme context.

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

// Create a Context
const ThemeContext = createContext();

// Create a Provider Component
export const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};
Step 2: Create a Custom Hook

Next, create a custom hook to use the context easily.

export const useTheme = () => {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
};
Step 3: Using the Provider in Your Application

Wrap your application (or part of it, depending which parts of you application will make use of the context) with the ThemeProvider to provide context to the components.

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { ThemeProvider } from './ThemeContext';

ReactDOM.render(
  <ThemeProvider>
    <App />
  </ThemeProvider>,
  document.getElementById('root')
);
Step 4: Consuming the Context with the Custom Hook

Now, you can easily access the theme and the toggle function in any component that is a descendant of the ThemeProvider.

import React from 'react';
import { useTheme } from './ThemeContext';

const ThemeToggler = () => {
  const { theme, toggleTheme } = useTheme();

  return (
    <div>
      <p>Current Theme: {theme}</p>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  );
};

export default ThemeToggler;
Visual flow of how Context API and custom hooks work
Visual flow of how Context API and custom hooks work

After implementing our theme context, we can see that some of the benefits using this approach are:

  1. Cleaner Code: Custom hooks reduce boilerplate code in components, making them cleaner and easier to understand.
  2. Enhanced Reusability: The useTheme hook can be reused across any number of components that need access to theme state.
  3. Easier Testing: Encapsulated logic in custom hooks can be tested independently from components, improving testability.

Conclusion

Using the Context API in combination with custom hooks provides a powerful approach to managing state in React applications. This pattern encourages better organization, reusability, and maintainability of your code, making it easier to manage global state effectively. By creating a custom hook for accessing context, you streamline your component logic and enhance the overall structure of your application.