← Back to Chapters

useContext() Hook

⚛️ useContext() Hook

React Hooks Context API No Prop Drilling

? Quick Overview

The useContext() Hook provides a simple and elegant way to read values from React’s Context API — without manually passing props through multiple component levels.

Context is designed to share global data such as user info, app settings, or theme state across components, helping you completely avoid “prop drilling.”

? Key Concepts

  • Context – A way to share data globally in a component tree.
  • createContext() – Creates a Context object.
  • Provider – Makes a value available to all nested components.
  • useContext() – Hook used to consume (read) the nearest context value.
  • Prop Drilling – Passing props down through many levels just to reach a deeply nested child.
  • Multiple Contexts – You can use several contexts together (auth, theme, language, etc.).

? The Problem: Prop Drilling

Without context, data often has to be passed through several intermediate components that don’t actually use it, making code cluttered and harder to maintain.

? View Code Example (Without Context)
// Example of prop drilling without Context
function App() {
  const user = "John";
  return <Parent user={user} />;
}

function Parent({ user }) {
  return <Child user={user} />;
}

function Child({ user }) {
  return <p>Hello, {user}!</p>;
}

? Explanation

The user value is needed only in the Child component, but it still has to be passed through Parent. As your component tree grows deeper, this pattern becomes painful and error-prone.

✅ Solution: Context + useContext()

With useContext(), you can access shared values directly from the nearest context provider, eliminating the need for prop drilling.

? View Code Example (Basic useContext)
// Using Context and useContext to avoid prop drilling
import React, { createContext, useContext } from "react";

// 1️⃣ Create Context
const UserContext = createContext();

// 2️⃣ Create Provider Component
function App() {
  return (
    <UserContext.Provider value="John">
      <Dashboard />
    </UserContext.Provider>
  );
}

// 3️⃣ Consume Context in Child Component
function Dashboard() {
  const user = useContext(UserContext);
  return <h5 className="text-center">Welcome, {user}!</h5>;
}

export default App;

? Explanation

Here, Dashboard directly reads the value from UserContext using useContext(UserContext). Intermediate components no longer need to pass the user as a prop.

? Syntax & Theory

Step 1: Create a Context

? View Code Example (createContext)
// Creating a context with an optional default value
const MyContext = createContext(defaultValue);

Step 2: Wrap components with a Provider

? View Code Example (Provider)
// Providing a value to all nested components
function App() {
  const value = "Some shared value";

  return (
    <MyContext.Provider value={value}>
      <ChildComponent />
    </MyContext.Provider>
  );
}

Step 3: Consume context with useContext()

? View Code Example (useContext Syntax)
// Reading the nearest MyContext value
function ChildComponent() {
  const value = useContext(MyContext);
  return <p>Value from context: {value}</p>;
}

?️ Example: Theme Switcher with useContext()

Let’s see how Context can share theme values (light/dark) and a toggle function across multiple components.

? View Code Example (ThemeContext)
// Sharing theme and toggleTheme using Context + useContext
import React, { createContext, useContext, useState } from "react";

// Create Context
const ThemeContext = createContext();

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

  const toggleTheme = () => {
    setTheme((prev) => (prev === "light" ? "dark" : "light"));
  };

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

// Component consuming context
function ThemedBox() {
  const { theme, toggleTheme } = useContext(ThemeContext);

  const styles = {
    backgroundColor: theme === "light" ? "#f8f9fa" : "#212529",
    color: theme === "light" ? "#212529" : "#f8f9fa",
    padding: "20px",
    borderRadius: "8px",
    textAlign: "center"
  };

  return (
    <div style={styles}>
      <p>Current Theme: {theme}</p>
      <button className="btn btn-outline-info" onClick={toggleTheme}>
        Toggle Theme
      </button>
    </div>
  );
}

// Root Component
function App() {
  return (
    <ThemeProvider>
      <ThemedBox />
    </ThemeProvider>
  );
}

export default App;

? Explanation

The ThemeContext provides both the current theme and a toggleTheme function to all components wrapped by ThemeProvider. Any component inside can switch the theme without needing props.

? Multiple Contexts Example

You can safely combine multiple contexts to share different types of global data (e.g., auth + language).

? View Code Example (Multiple Contexts)
// Using AuthContext and LangContext together
const AuthContext = createContext();
const LangContext = createContext();

function App() {
  return (
    <AuthContext.Provider value="John">
      <LangContext.Provider value="English">
        <Profile />
      </LangContext.Provider>
    </AuthContext.Provider>
  );
}

function Profile() {
  const user = useContext(AuthContext);
  const lang = useContext(LangContext);

  return <p>{user} is viewing this page in {lang}.</p>;
}

? Explanation

Each useContext() call reads from a different provider. This lets you keep concerns separated (authentication, theme, language, etc.) while still avoiding prop drilling.

? When to Use useContext()

  • ✅ Sharing global data like user, theme, language, or settings.
  • ✅ Avoiding repetitive prop drilling across deeply nested components.
  • ✅ Centralizing app-wide state for small and medium-sized apps.
  • ❌ Avoid using context for very frequently changing values, as it can cause many re-renders.

⚖️ useContext() vs Prop Drilling

Aspect Prop Drilling useContext()
Data Sharing Passed manually through each level Available via nearest Provider
Ease of Maintenance Harder as the tree grows Simpler and more scalable
Performance Good for isolated props Context updates re-render all consumers

? Tips & Best Practices

  • Use descriptive names like ThemeContext, UserContext, or AuthContext.
  • Wrap your entire app or specific sections in context providers for shared data.
  • Combine context with custom hooks (e.g., useTheme(), useAuth()) for cleaner code.
  • Avoid deeply nesting many providers; group related contexts when possible.
  • For very large or complex apps, consider state libraries like Redux or Zustand along with or instead of context.

? Try It Yourself

  1. Create a UserContext to store and display user info across multiple components.
  2. Build a LanguageContext that switches between English and Hindi text.
  3. Use multiple contexts together (e.g., AuthContext + ThemeContext).
  4. Extract your context logic into a custom hook like useTheme() or useAuth().

Goal: Understand how useContext() offers a simple and powerful way to share global data across React components without tedious prop drilling.