Skip to content
intermediatePhase 36 · React

Context API

Share state across components without prop drilling using React Context.

45m
0 problems
Topic Progress0%

Creating Context

Creating Context

Context provides a way to pass data through the component tree without props drilling.

Basic Context

import { createContext, useContext, useState } from "react";

// Create context
const ThemeContext = createContext(null);

// 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>
  );
}

// Custom hook for using context
function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error("useTheme must be used within a ThemeProvider");
  }
  return context;
}

// Usage
function App() {
  return (
    <ThemeProvider>
      <Header />
      <Main />
    </ThemeProvider>
  );
}

function Header() {
  const { theme, toggleTheme } = useTheme();
  return (
    <header>
      <button onClick={toggleTheme}>Toggle Theme</button>
      <span>Current: {theme}</span>
    </header>
  );
}

Default Values

// With default value
const ThemeContext = createContext("light");

// Without provider, uses default value
function Component() {
  const theme = useContext(ThemeContext); // "light"
}

Multiple Contexts

const AuthContext = createContext(null);
const ThemeContext = createContext(null);

function App() {
  return (
    <AuthProvider>
      <ThemeProvider>
        <Layout />
      </ThemeProvider>
    </AuthProvider>
  );
}

Provider Pattern

Provider Pattern

Complete Provider Example

import { createContext, useContext, useState, useMemo } from "react";

const AuthContext = createContext(null);

function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const login = async (email, password) => {
    try {
      setLoading(true);
      setError(null);
      const response = await fetch("/api/login", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email, password })
      });
      const data = await response.json();
      setUser(data.user);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  const logout = () => {
    setUser(null);
  };

  // Memoize value to prevent unnecessary re-renders
  const value = useMemo(
    () => ({
      user,
      loading,
      error,
      login,
      logout,
      isAuthenticated: !!user
    }),
    [user, loading, error]
  );

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

// Custom hook
function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error("useAuth must be used within an AuthProvider");
  }
  return context;
}

Nested Providers

function App() {
  return (
    <AuthProvider>
      <ThemeProvider>
        <NotificationProvider>
          <Layout />
        </NotificationProvider>
      </ThemeProvider>
    </AuthProvider>
  );
}

// Each component can access any provider
function Header() {
  const { user } = useAuth();
  const { theme } = useTheme();
  const { notifications } = useNotification();
}

Provider Composition

// Combine multiple providers
function AppProviders({ children }) {
  return (
    <AuthProvider>
      <ThemeProvider>
        <NotificationProvider>
          {children}
        </NotificationProvider>
      </ThemeProvider>
    </AuthProvider>
  );
}

// Usage
function App() {
  return (
    <AppProviders>
      <Layout />
    </AppProviders>
  );
}

useContext Hook

useContext Hook

Basic Usage

const ThemeContext = createContext("light");

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>Click me</button>;
}

Accessing Context Values

function UserProfile() {
  const { user, logout } = useAuth();
  const { theme, toggleTheme } = useTheme();

  if (!user) return <p>Please log in</p>;

  return (
    <div>
      <h2>{user.name}</h2>
      <button onClick={logout}>Logout</button>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  );
}

Context with Performance

// Problem: All consumers re-render when any value changes
const ValueContext = createContext({ a: 0, b: 0 });

// Solution: Split into separate contexts
const AContext = createContext(0);
const BContext = createContext(0);

function Provider({ children }) {
  const [a, setA] = useState(0);
  const [b, setB] = useState(0);

  return (
    <AContext.Provider value={a}>
      <BContext.Provider value={b}>
        {children}
      </BContext.Provider>
    </AContext.Provider>
  );
}

// Now components only re-render when their specific value changes
function ComponentA() {
  const a = useContext(AContext); // Only re-renders when a changes
}

function ComponentB() {
  const b = useContext(BContext); // Only re-renders when b changes
}

Context vs Redux

Feature Context Redux
Complexity Simple Complex
Bundle size Small Larger
DevTools No Yes
Middleware No Yes
Performance Good for simple Optimized

Use Context for:

  • Theme, language, auth
  • Simple global state
  • Application settings

Use Redux for:

  • Complex state logic
  • Frequent updates
  • Need for devtools/middleware

Practice Problems

0/3solved
Build Context API Component

Create a reusable React component implementing Context API. Include proper state management and accessibility.

Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where needed
Context API Testing

Write unit and integration tests for Context API using React Testing Library.

Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility tests
Context API Performance

Optimize Context API for performance. Consider memoization, code splitting, and bundle size.

Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysis

Quiz

1. What is React Context used for?

Question 1 options

2. What does useContext return?

Question 2 options

3. What happens when context value changes?

Question 3 options

4. When should you use Context?

Question 4 options

Flashcards

Question

What is React Context?

Answer

A way to pass data through component tree without props drilling

Question

What does useContext return?

Answer

The value of the nearest Context Provider

Question

When do context consumers re-render?

Answer

When the context value changes

Question

When should you use Context vs Redux?

Answer

Context for simple global state, Redux for complex state logic

Question

What is Context API?

Answer

Context API is a key concept in frontend development.

Revision Notes

Key Takeaways

  • 1.Context avoids props drilling
  • 2.useContext consumes context values
  • 3.All consumers re-render when value changes
  • 4.Split contexts for performance
  • 5.Use for simple global state

Interview Tips

  • Explain when to use Context vs Redux
  • Show how to create a Context Provider
  • Demonstrate avoiding props drilling

Cheat Sheet

Cheat Sheet

Create Context

const ThemeContext = createContext(null);

Provider

<ThemeContext.Provider value={{ theme, toggle }}>
  {children}
</ThemeContext.Provider>

useContext

const { theme, toggle } = useContext(ThemeContext);

Custom Hook

function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) throw new Error("...");
  return context;
}