Exploring React Hooks: A Comprehensive Tutorial Series — Part 5: useContext

Elevate Your React Development with Hooks — Simplifying State and Lifecycle Management

Vinojan Veerapathirathasan
6 min readJul 4, 2024
React Hooks: useContext

Hi there 👋,

Welcome to the fifth part of the React Hooks series! In this part we will explore about useContext hook with the practical explanation. Let’s deep dive into the topic 🎊

useContext

The useContext hook is a powerful tool in React's Hooks API that simplifies the management and sharing of state across different components without resorting to prop drilling. This hook allows components to subscribe to React Contexts directly, enabling them to access the context values without having to pass props through multiple levels of the component hierarchy. useContext makes it easier to maintain cleaner code and promotes component reusability.

Development Needs for useContext

In larger React applications, managing global states such as themes, user settings, or authentication status can become cumbersome if these states are passed manually from parent-to-child components. This not only bloats the component tree with unnecessary props but also complicates component re-usability and testing. useContext addresses these issues by providing a more direct way to share such states across the entire application, or between specific parts of it, without tightly coupling the components to their parents.

Figure 1: without Context vs with Context

Example 1:

Let’s consider a simple example of a theme toggler that uses useContext to manage and apply a theme across multiple components:

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

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

function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
};

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

function Button() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button onClick={toggleTheme}>
Switch to {theme === 'light' ? 'dark' : 'light'} mode
</button>
);
}

export default function App() {
return (
<ThemeProvider>
<Button />
</ThemeProvider>
);
}

In this example, we’ll explore how to use useContext in React to create a theme toggler that is accessible across multiple components in an application. This involves setting up a ThemeContext to provide the current theme and a method to toggle the theme, and then using this context in separate components to change and display the theme.

  • First, we create a context that will hold the theme information. The context will store not only the current theme but also a function to toggle the theme.
  • The ThemeProvider component will act as the provider for the theme context. It manages the theme state and provides the theme and toggleTheme function to the rest of the application. This component will wrap the main part of your application so that all child components can access the theme context.
  • The Button component will be responsible for showing and toggling the theme. It also accesses the theme context to get the current theme value.

Example 2:

In this example, we’ll construct a user preference system in a React application that manages theme and language settings across multiple components. We’ll use useContext to propagate these preferences throughout the application, ensuring that changes in one component are reflected globally without direct coupling of the components. This setup involves creating a context to hold the preferences, a provider to manage and update these preferences, and several consumer components that react to changes in these preferences.

App.js

└─── PreferencesProvider.js

├─── ThemeSwitcher.js

├─── LanguageSelector.js

└─── DisplaySettings.js

#1 : Creating the Context

First, let’s define a PreferencesContext that will hold the user's settings. We'll place this context in its own file to keep our codebase organized.

// PreferencesContext.js

import { createContext } from 'react';

const PreferencesContext = createContext({
theme: 'light', // Default theme
language: 'English', // Default language
setTheme: () => {},
setLanguage: () => {}
});

export default PreferencesContext;

#2 : Building the Provider

Next, we will create a PreferencesProvider component. This component will encapsulate the state management logic and provide the context to the rest of the application. It's responsible for updating the context values based on user interactions.

// PreferencesProvider.js

import React, { useState } from 'react';
import PreferencesContext from './PreferencesContext';

function PreferencesProvider({ children }) {
const [theme, setTheme] = useState('light');
const [language, setLanguage] = useState('English');

const handleSetTheme = newTheme => setTheme(newTheme);
const handleSetLanguage = newLanguage => setLanguage(newLanguage);

return (
<PreferencesContext.Provider value={{ theme, language, setTheme: handleSetTheme, setLanguage: handleSetLanguage }}>
{children}
</PreferencesContext.Provider>
);
}

export default PreferencesProvider;

#3 : Consumer Components

Now, let’s create several components that will use these preferences. Each component will access the PreferencesContext to either display or modify the preferences.

// ThemeSwitcher.js

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

function ThemeSwitcher() {
const { theme, setTheme } = useContext(PreferencesContext);

return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Switch to {theme === 'light' ? 'dark' : 'light'} mode
</button>
);
}

export default ThemeSwitcher;
// LanguageSelector.js

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

function LanguageSelector() {
const { language, setLanguage } = useContext(PreferencesContext);

return (
<select value={language} onChange={(e) => setLanguage(e.target.value)}>
<option value="English">English</option>
<option value="Spanish">Spanish</option>
<option value="French">French</option>
</select>
);
}

export default LanguageSelector;
// DisplaySettings.js

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

function DisplaySettings() {
const { theme, language } = useContext(PreferencesContext);

return (
<div>
<p>Current Theme: {theme}</p>
<p>Current Language: {language}</p>
</div>
);
}

export default DisplaySettings;

#4 : Integrating the Components in the Application

Finally, let’s bring all these components together in the main application file.

// App.js

import React from 'react';

import PreferencesProvider from './PreferencesProvider';
import ThemeSwitcher from './ThemeSwitcher';
import LanguageSelector from './LanguageSelector';
import DisplaySettings from './DisplaySettings';

function App() {
return (
<PreferencesProvider>
<ThemeSwitcher />
<LanguageSelector />
<DisplaySettings />
</PreferencesProvider>
);
}

export default App;

How the Frontend Development Became Easier After the useContext

useContext has significantly eased the development of React applications by:

  • 🪛 Reducing Prop Drilling: It eliminates the need to pass props through multiple levels, keeping the component API clean and focused on the components’ own logic.
  • Enhancing Code Maintainability and Reusability: By decoupling the state management from component trees, components become more modular and easier to maintain or reuse.
  • 🪄 Simplifying State Management: It provides a straightforward way to share state across many components, which is particularly useful in scenarios like theming, user authentication, or language preferences.

As we Having explored useContext and its role in streamlining state management across components.

I appreciate you taking the time to read this article.🙌

🔔 Stay tuned for the next article in this series, where we will dive into useRef. This hook offers a way to persist values across renders without triggering re-renders, and it can also be used to reference DOM elements directly.

Before you move on to explore next article, don’t forget to give your claps 👏 for this article and leave your feedback💭 on the comment section.
Stay connected with me on social media. Thanks for your support and have a great rest of your day! 🎊

Part 1: useState — https://medium.com/@imvinojanv/exploring-react-hooks-a-comprehensive-tutorial-series-part-1-usestate-3c1ce8378e95

Part 2: useEffect — https://medium.com/@imvinojanv/exploring-react-hooks-a-comprehensive-tutorial-series-part-2-useeffect-dd2d63778af8

Part 3: useMemo — https://medium.com/@imvinojanv/exploring-react-hooks-a-comprehensive-tutorial-series-part-3-usememo-6c694f485d95

Part 4: useCallback — https://medium.com/@imvinojanv/exploring-react-hooks-a-comprehensive-tutorial-series-part-4-usecallback-1dd116866228

✍️ Vinojan Veerapathirathasan.
LinkedIn : https://www.linkedin.com/in/imvinojanv/
Email: imvinojanv@gmail.com

--

--

Vinojan Veerapathirathasan

Software Engineer at EL | Designer | Medium Writer | AI Enthusiast | Entrepreneur | Specializing in Modern Web Application Development