Back to Course

Learn React

0% Complete
0/0 Steps
Lesson 19 of 32
In Progress

Introduction to the Context API

The Context API is a powerful tool in React that allows you to pass data down the component tree without having to manually pass props down at every level. This can be especially useful when you have data that needs to be accessed by multiple components, but is not directly related to the rendering of those components.

In this lesson, we will learn how to set up a context in React and how to use the useContext hook to access the context in functional components. We will also learn how to update the context using the useReducer hook and the Context.Provider component.

Setting Up a Context

To set up a context in React, we first need to create a context object using the createContext function. This function takes an optional default value as an argument, which will be used as the initial value of the context.

import { createContext } from 'react';

const MyContext = createContext();

Next, we need to wrap the components that will have access to the context in a Context.Provider component. The Context.Provider component takes a value prop, which is the value that will be provided to the context.

import { MyContext } from './MyContext';

function App() {
  return (
    <MyContext.Provider value={/* some value */}>
      <MyComponent />
    </MyContext.Provider>
  );
}

Accessing the Context in a Functional Component

To access the context in a functional component, we can use the useContext hook. The useContext hook takes the context object as an argument and returns the current value of the context.

import { useContext } from 'react';
import { MyContext } from './MyContext';

function MyComponent() {
  const value = useContext(MyContext);

  return (
    <div>{value}</div>
  );
}

Updating the Context with the useReducer Hook

The useReducer hook is a powerful way to manage state in React. It allows you to specify a reducer function and an initial state, and it returns the current state and a dispatch function. The dispatch function can be used to update the state by calling the reducer function with an action.

To use the useReducer hook with a context, we can wrap the component that needs to update the context in a Context.Provider component and pass the state and dispatch function as the value prop.

import { useReducer } from 'react';
import { MyContext } from './MyContext';

function MyComponent() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <MyContext.Provider value={{ state, dispatch }}>
      {/* component JSX goes here */}
    </MyContext.Provider>
  );
}

Conclusion

The Context API is a powerful tool in React that allows you to pass data down the component tree without having to manually pass props down at every level. It is especially useful when you have data that needs to be accessed by multiple components, but is not directly related to the rendering of those components. With the useContext and useReducer hooks, it is easy to access and update the context in functional components.

Exercises

To review these concepts, we will go through a series of exercises designed to test your understanding and apply what you have learned.

Create a context for storing a user’s favorite color and a component for displaying the user’s favorite color. Use the useContext hook to access the user’s favorite color in the component.

First, create the context object using the createContext function:

import { createContext } from 'react';

const ColorContext = createContext();

Next, create a component that displays the user’s favorite color using the useContext hook:

import { useContext } from 'react';
import { ColorContext } from './ColorContext';

function FavoriteColor() {
  const { color } = useContext(ColorContext);

  return (
    <div>Your favorite color is: {color}</div>
  );
}

Finally, wrap the FavoriteColor component in a Context.Provider component and pass the user’s favorite color as the value prop:

import { ColorContext } from './ColorContext';

function App() {
  return (
    <ColorContext.Provider value={{ color: 'blue' }}>
      <FavoriteColor />
    </ColorContext.Provider>
  );
}

Create a context for storing a list of items and a component for adding items to the list. Use the useReducer hook to update the list of items in the context.

First, create the context object using the createContext function:

import { createContext } from 'react';

const ItemContext = createContext();

Next, create a reducer function that handles updates to the list of items:

function itemReducer(state, action) {
  switch (action.type) {
    case 'ADD_ITEM':
      return [...state, action.item];
    case 'REMOVE_ITEM':
      return state.filter(i => i !== action.item);
    default:
      return state;
  }
}

Then, create a component that allows the user to add items to the list using the useReducer hook:

import { useReducer } from 'react';
import { ItemContext } from './ItemContext';

function AddItem() {
  const [items, dispatch] = useReducer(itemReducer, []);

  function handleSubmit(event) {
    event.preventDefault();
    const formData = new FormData(event.target);
    const item = formData.get('item');
    dispatch({ type: 'ADD_ITEM', item });
  }

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="item">Item:</label>
      <input type="text" name="item" />
      <button type="submit">Add Item</button>
    </form>
  );
}

Finally, wrap the AddItem component in a Context.Provider component and pass the items and dispatch function as the value prop:

import { ItemContext } from './ItemContext';

function App() {
  return (
    <ItemContext.Provider value={{ items, dispatch }}>
      <AddItem />
    </ItemContext.Provider>
  );
}

Create a context for storing a user’s login status and a component for displaying a login form. Use the useContext and useState hooks to manage the user’s login status in the component.

First, create the context object using the createContext function:

import { createContext } from 'react';

const LoginContext = createContext();

Next, create a component that displays a login form using the useContext and useState hooks:

import { useContext, useState } from 'react';
import { LoginContext } from './LoginContext';

function LoginForm() {
  const { login, setLogin } = useContext(LoginContext);
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  function handleSubmit(event) {
    event.preventDefault();
    setLogin(true);
  }

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="username">Username:</label>
      <input type="text" name="username" value={username} onChange={event => setUsername(event.target.value)} />
      <label htmlFor="password">Password:</label>
      <input type="password" name="password" value={password} onChange={event => setPassword(event.target.value)} />
      <button type="submit">Log In</button>
    </form>
  );
}

Finally, wrap the LoginForm component in a Context.Provider component and pass the login status and setLogin function as the value prop:

import { LoginContext } from './LoginContext';

function App() {
  const [login, setLogin] = useState(false);

  return (
    <LoginContext.Provider value={{ login, setLogin }}>
      <LoginForm />
    </LoginContext.Provider>
  );
}

Create a context for storing a user’s language preference and a component for displaying a language selection form. Use the useContext and useReducer hooks to manage the user’s language preference in the component.

First, create the context object using the createContext function:

import { createContext } from 'react';

const LanguageContext = createContext();

Next, create a reducer function that handles updates to the user’s language preference:

function languageReducer(state, action) {
  switch (action.type) {
    case 'SET_LANGUAGE':
      return action.language;
    default:
      return state;
  }
}

Then, create a component that displays a language selection form using the useContext and useReducer hooks:

import { useContext, useReducer } from 'react';
import { LanguageContext } from './LanguageContext';

function LanguageSelector() {
  const [language, dispatch] = useReducer(languageReducer, 'english');

  function handleChange(event) {
    dispatch({ type: 'SET_LANGUAGE', language: event.target.value });
  }

  return (
    <form>
      <label htmlFor="language">Language:</label>
      <select name="language" value={language} onChange={handleChange}>
        <option value="english">English</option>
        <option value="french">French</option>
        <option value="spanish">Spanish</option>
      </select>
    </form>
  );
}

Finally, wrap the LanguageSelector component in a Context.Provider component and pass the language and dispatch function as the value prop:

import { LanguageContext } from './LanguageContext';

function App() {
  const [language, dispatch] = useReducer(languageReducer, 'english');

  return (
    <LanguageContext.Provider value={{ language, dispatch }}>
      <LanguageSelector />
    </LanguageContext.Provider>
  );
}

Create a context for storing a user’s theme preference (light or dark) and a component for displaying a theme toggle button. Use the useContext and useState hooks to manage the user’s theme preference in the component.

First, create the context object using the createContext function:

import { createContext } from 'react';

const ThemeContext = createContext();

Next, create a component that displays a theme toggle button using the useContext and useState hooks:

import { useContext, useState } from 'react';
import { ThemeContext } from './ThemeContext';

function ThemeToggle() {
  const { theme, setTheme } = useContext(ThemeContext);

  function handleClick() {
    setTheme(theme === 'light' ? 'dark' : 'light');
  }

  return (
    <button onClick={handleClick}>Toggle Theme</button>
  );
}

Finally, wrap the ThemeToggle component in a Context.Provider component and pass the theme and setTheme functions as the value prop:

import { ThemeContext } from './ThemeContext';

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

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