intro-frontend-course

Context API and React’s useContext Hook


1. Introduction to the Context API

The Context API in React is a powerful tool that allows you to manage and share state across multiple components without passing props down through every level of the component tree. It’s particularly useful for global state management, where data needs to be accessible by many components at different levels of your application.

Key Concepts:

Basic Example of Creating and Using Context:
import React, { createContext, useState } from 'react';

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

function App() {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider value=>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  return (
    <ThemeContext.Consumer>
      {({ theme, setTheme }) => (
        <button
          onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
          style=
        >
          Toggle Theme
        </button>
      )}
    </ThemeContext.Consumer>
  );
}

export default App;

In this example, the ThemeContext provides the current theme and a function to toggle it. The ThemedButton component consumes this context to adjust its appearance and functionality based on the theme.


2. Using useContext to Manage Global State

While the above example uses the Context.Consumer pattern, a more modern and convenient way to access context is by using the useContext hook. This hook simplifies accessing the context value, making your code more readable and concise.

2.1. Creating and Using Context with useContext

Example:

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

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

function App() {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider value=>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const { theme, setTheme } = useContext(ThemeContext); // Using useContext to consume the context

  return (
    <button
      onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
      style=
    >
      Toggle Theme
    </button>
  );
}

export default App;

In this refactored example, the ThemedButton component uses the useContext hook to access the theme and setTheme directly from the ThemeContext, eliminating the need for a render prop or nested function.

2.2. Benefits of useContext

3. Refactoring a Project to Use the Context API

Refactoring an existing project to use the Context API involves identifying areas where state or functions are passed down through multiple levels of components and replacing those props with context.

3.1. Identify State and Prop Drilling

Prop Drilling: Occurs when data is passed from a parent component to a child component that doesn’t need it, just to get the data to a more deeply nested component. This can make your code harder to manage and debug.

Example Before Refactoring:

function App() {
  const [user, setUser] = useState({ name: 'John Doe' });

  return (
    <div>
      <Header user={user} />
      <Profile user={user} setUser={setUser} />
    </div>
  );
}

function Header({ user }) {
  return <h1>Welcome, {user.name}!</h1>;
}

function Profile({ user, setUser }) {
  return (
    <div>
      <p>Name: {user.name}</p>
      <button onClick={() => setUser({ name: 'Jane Doe' })}>Change Name</button>
    </div>
  );
}

In this example, the user and setUser props are passed down through multiple components, even if not all components need them directly.

3.2. Refactor with Context API

After refactoring with the Context API, the state can be shared across components without explicitly passing it down as props.

Example After Refactoring:

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

// Create a UserContext
const UserContext = createContext();

function App() {
  const [user, setUser] = useState({ name: 'John Doe' });

  return (
    <UserContext.Provider value=>
      <Header />
      <Profile />
    </UserContext.Provider>
  );
}

function Header() {
  const { user } = useContext(UserContext); // Using useContext to consume UserContext
  return <h1>Welcome, {user.name}!</h1>;
}

function Profile() {
  const { user, setUser } = useContext(UserContext); // Using useContext to consume UserContext
  return (
    <div>
      <p>Name: {user.name}</p>
      <button onClick={() => setUser({ name: 'Jane Doe' })}>Change Name</button>
    </div>
  );
}

export default App;

In this refactored example:

3.3. Testing and Debugging

After refactoring, it’s important to test your components to ensure that they correctly access and modify the global state using the Context API. Use React Developer Tools to inspect context values and ensure everything is functioning as expected.


4. Video Resources

To reinforce your understanding of the Context API and useContext, here are some helpful videos:

React Context API Explained (10:12)
React useContext Hook (7:28)
Refactoring with Context API (9:45)

5. External Resources

For further exploration and a deeper understanding: