useContext: React Hooks

Harsh Mishra - Nov 8 - - Dev Community

useContext in React with Two Practical Mini-Projects

Introduction

In React, managing data between components is essential, especially in larger applications where multiple components need access to the same data. Prop drilling—passing props down multiple levels of a component tree—can quickly become cumbersome. This is where React's useContext hook shines. useContext allows you to share data across components without manually passing props, making it an invaluable tool for state management.

In this article, we’ll start with a detailed explanation of useContext, its syntax, and its benefits. Then, we’ll solidify this understanding by building two mini-projects:

  1. Theme Switcher: A simple theme toggler to show how useContext manages global state.
  2. User Authentication Status: An app to handle user login state, demonstrating how to use useContext for real-world use cases.

By the end of this tutorial, you’ll be well-equipped to use useContext confidently in any React project.


What is useContext?

The Basics of useContext

useContext is a React hook that allows components to subscribe to a context directly. It helps avoid the hassle of prop drilling by enabling components to access global data from the nearest provider above it in the component tree.

Syntax of useContext

Here’s the basic syntax for creating and using a context:

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

const MyContext = createContext(defaultValue); // Step 1: Create a context

function MyComponent() {
    const contextValue = useContext(MyContext); // Step 2: Use the context
    return <div>{contextValue}</div>;
}
Enter fullscreen mode Exit fullscreen mode

Explanation

  1. Creating a Context: createContext initializes a context object, which holds the data we want to share. The defaultValue parameter is optional but can be used as a fallback if no Provider is found.
  2. Using the Context with useContext: Inside a component, we use useContext(MyContext) to access the context's current value. This value is determined by the nearest <MyContext.Provider> above the component in the component tree.

Example of a Context Provider and Consumer

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

const ThemeContext = createContext('light'); // default theme is light

function ThemeProvider({ children }) {
    return <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>;
}

function DisplayTheme() {
    const theme = useContext(ThemeContext); // Consuming the context
    return <p>The current theme is {theme}</p>;
}

function App() {
    return (
        <ThemeProvider>
            <DisplayTheme />
        </ThemeProvider>
    );
}
Enter fullscreen mode Exit fullscreen mode

In this example:

  • ThemeContext is our context, initialized with a default value of 'light'.
  • ThemeProvider wraps DisplayTheme and provides a value="dark", making 'dark' the current theme within the ThemeProvider.
  • DisplayTheme component uses useContext(ThemeContext) to access the theme and render it.

This covers the basics. Now, let’s dive into the projects to apply this knowledge in practical scenarios.


Mini Project 1: Building a Theme Switcher

Our first project is a simple theme switcher that will demonstrate how useContext can be used to manage global application state for themes.

Step 1: Set Up the Context

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

const ThemeContext = createContext();

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

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

Here, ThemeContext provides two values: the current theme and a function to toggle it. The provider wraps the app components, making the theme and toggle function available globally.

Step 2: Consume the Context in Components

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

function DisplayTheme() {
    const { theme } = useContext(ThemeContext);
    return <p>Current Theme: {theme}</p>;
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Use the Provider in the Main App

function App() {
    return (
        <ThemeProvider>
            <DisplayTheme />
            <ThemeToggler />
        </ThemeProvider>
    );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Now, you can toggle between light and dark themes by clicking the button, with the theme status displayed alongside. This project demonstrates how useContext allows multiple components to share and react to global state changes.


Mini Project 2: Managing User Authentication

For the second project, let’s build a simple app that tracks a user’s authentication status using useContext.

Step 1: Create an Auth Context

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

const AuthContext = createContext();

export function AuthProvider({ children }) {
    const [isAuthenticated, setIsAuthenticated] = useState(false);

    const login = () => setIsAuthenticated(true);
    const logout = () => setIsAuthenticated(false);

    return (
        <AuthContext.Provider value={{ isAuthenticated, login, logout }}>
            {children}
        </AuthContext.Provider>
    );
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Create Login and Logout Components

function LoginButton() {
    const { login } = useContext(AuthContext); // Access login function
    return <button onClick={login}>Login</button>;
}

function LogoutButton() {
    const { logout } = useContext(AuthContext); // Access logout function
    return <button onClick={logout}>Logout</button>;
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Display User Status

function UserStatus() {
    const { isAuthenticated } = useContext(AuthContext);
    return (
        <p>{isAuthenticated ? 'Welcome back!' : 'Please log in.'}</p>
    );
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Combine Components in App

function App() {
    return (
        <AuthProvider>
            <UserStatus />
            <LoginButton />
            <LogoutButton />
        </AuthProvider>
    );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Now, you have a simple authentication status app where the login and logout buttons update the user’s status across the app. This project demonstrates how useContext can handle state across an application in real-world scenarios.


Conclusion

With these two projects, you’ve seen how useContext simplifies data sharing between components without the need for prop drilling. The theme switcher and authentication status projects give practical insights into managing global state effectively. Whether you’re toggling themes or handling user authentication, useContext provides a powerful tool to build efficient and organized applications.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .