Marcy Lab School Docs
  • Welcome
  • Student Guidelines & Policies
    • Student Handbook
    • AI Policy
    • Academic Calendar
  • Environment Setup
    • Local Environment Setup - Mac
    • Local Environment Setup - Windows
    • GitHub Setup
    • Postgres Setup
  • How-Tos
    • How To Code at Marcy: Code Style Guide
    • How to Do Short Response and Coding Assignments
    • How to Debug
    • How to PEDAC
    • How to Create A GitHub Organization and Scrumboard
    • How to Create Projects with Vite
    • How to Deploy on GitHub Pages
    • How to Deploy on Render
    • How to Test your API with Postman
  • Mod 0 - Command Line Interfaces, Git, and GitHub
    • Overview
    • 1. Command Line Interfaces
    • 2. Git & GitHub
    • 3. Git Pulling & Merging
    • 4. Git Branching & PRs
  • Mod 1 - JavaScript Fundamentals
    • Overview
    • 1. Intro to Programming
    • 2. Errors
    • 3. Node & Node Modules
    • 4. Variables, Functions & String Methods
    • 5. Control Flow, typeof, and Math
    • 6. Loops
    • 7. Arrays
    • 8. Objects
    • 9. Higher Order Functions: Callbacks
    • 10. Higher Order Functions: Array Methods
    • 11. Regex
  • Mod 2 - HTML, CSS & the DOM
    • Overview
    • 1. HTML
    • 2. CSS
    • 3. Accessibility (a11y)
    • 4. The Document Object Model (DOM) API
    • 5. Events
    • 6. Forms
    • 7. The Box Model and Positioning
    • 8. Flexbox
    • 9. Grid & Media Queries
    • 10. ESModules
    • 11. Vite
    • 12. LocalStorage
  • Mod 3 - Async & APIs
    • Overview
    • 1. Promises
    • 2. Fetch
    • 3. Building a Fetching App
    • 4. Async & Await
    • 5. A Generic Fetch Handler
  • Mod 4 - Project Week!
    • Important How Tos and Guides
      • How to Create a GitHub Organization and Scrum Board
      • How To Start a Project with Vite
      • How To Deploy a Project with GitHub Pages
    • Project Week Overview
    • Agile Methodologies
    • Deliverables & Milestones
    • Technical Requirements Checklist
    • Free API List
    • Collaborative GitHub
  • Mod 5 - Object-Oriented Programming
    • Overview
    • 1. Intro to OOP, Encapsulation, Factory Functions, and Closure
    • 2. Classes
    • 3. Private & Static
    • 4. UML Diagrams & Has Many/Belongs To Relationships
    • 5. Challenge: Implementing Has Many/Belongs To
    • 6. Inheritance
    • 7. Polymorphism
    • 8. Review and Practice
    • MDN: Object Prototypes
  • Mod 6 - Data Structures & Algorithms
    • Overview
    • Important How Tos and Guides
      • How to Debug
      • How to PEDAC
    • 1. Nodes & Linked Lists
    • 2. Singly & Doubly Linked Lists
    • 3. Stacks & Queues
    • 4. Recursion
    • 5. Trees
  • Mod 7 - React
    • Overview
    • Important How Tos and Guides
      • How to Create Projects with Vite
      • How to Deploy on GitHub Pages
    • 1. Intro to React
    • 2. Events, State, and Forms
    • 3. Fetching with useEffect
    • 4. React Router
    • 5. Building a Flashcards App
    • 6. React Context
    • 7. Global Context Pattern
  • Mod 8 - Backend
    • Overview
    • Important How Tos and Guides
      • How to Deploy on Render
      • How to Test your API with Postman
      • Postgres Setup
    • 1. Intro to Express
    • 2. Building a Static Web Server with Middleware
    • 3. Securing API Keys and Environment Variables
    • 4. RESTful CRUD API
    • 5. Model-View-Controller Architecture
    • 6. SQL and Databases
    • 7. JOIN (Association) SQL Queries
    • 8. Knex
    • 9. Your First Fullstack App!
    • 10. Migrations & Seeds
    • 11. Schema Design & Normalization
    • 12. Hashing Passwords with Bcrypt
    • 13. React Express Auth Template Overview
  • Mod 9 - Civic Tech Hackathon
    • Overview
    • Rubric
  • Mod 10 - Capstone
    • Overview
Powered by GitBook
On this page
  • Terms
  • Problem: Props Drilling
  • Passing State From App to LikesButton
  • Solution: useContext
  • 1. Create a context object
  • 2. Render a Context Provider
  • 3. Use the Context
  • Summary
  1. Mod 7 - React

6. React Context

Previous5. Building a Flashcards AppNext7. Global Context Pattern

Last updated 8 months ago

Follow along with code examples !

Table of Contents:

Terms

  • Context (English Definition) — The circumstances that form the setting for an event, statement, or idea, and in terms of which it can be fully understood and assessed.

  • Props Drilling - When we pass a piece of state through 2 or more child components before reaching the component that uses the state.

  • React Context — the combination of:

    1. an object where we can store global data and

    2. a "wrapper" component that provides its data to all descendants

  • createContext — A function exported from react that creates a new Context object.

  • Context.Provider — A component created from a Context object that provides all of its descendants with access to the Context object's values.

  • useContext — A React hook for retrieving the values of a Context object.

Problem: Props Drilling

Consider the instagram clone app that renders something like this:

Note how there is a total tally of likes at the top that sums the likes on each individual picture.

Passing State From App to LikesButton

To achieve the totalLikes feature, The App component defines a piece of state called totalLikes and a function incrementTotalLikes that invokes setTotalLikes.

const App = () => {
  const [totalLikes, setTotalLikes] = useState(0);
  const incrementTotalLikes = () => {
    setTotalLikes((totalLikes) => totalLikes + 1);
  }

  return (
    <>
      <Header likes={totalLikes} />
      <PicturesList incrementTotalLikes={incrementTotalLikes} />
    </>
  );
};

However, the component that uses incrementTotalLikes is LikesButton which is 3 layers away from App.

  • So we first pass incrementTotalLikes to PicturesList...

  • Which passes it to each InstagramPost instance...

  • Which passes it down to LikesButton...

  • Which finally uses it.

const LikesButton = ({ incrementTotalLikes }) => {
  const [likes, setLikes] = useState(0);

  const handleClick = () => {
    incrementTotalLikes();
    setLikes(likes + 1)
  }

  return (
    <div className="likes-container">
      <button onClick={handleClick}>❤️ {likes}</button>
    </div>
  )
}

This is called props drilling — when we pass a piece of state through 2 or more child components before reaching the component that uses the state.

It is okay to pass a piece of state through 1 intermediate component but passing state through 2 or more can start to feel tedious.

Solution: useContext

The solution is to create something called a Context.

In plain english, "context" means:

the circumstances that form the setting for an event, statement, or idea, and in terms of which it can be fully understood and assessed.

In React, Context is the combination of:

  1. an object where we can store global data and

  2. a "wrapper" component that provides its data to all descendants

To use React's Context API, there are 3 concepts to understand.

  • Making a Context object - const Context = createContext(startingValue)

  • Rendering a Context Provider - <Context.Provider values={values} />

  • Getting values from a Context - useContext(Context)

1. Create a context object

This is certainly the simplest step. It will almost always look like this:

// src/context/InstagramContext.jsx
import { createContext } from "react";

const defaultValue = {}

const InstagramContext = createContext({});

export default InstagramContext;
  • We create a new folder called context/ in our src folder

  • createContext is a named export of the react library

  • We invoke createContext to create a new InstagramContext object which we export.

  • It can take in a defaultValue argument. For now, we'll just use an empty object.

The InstagramContext object is the "glue" that makes context possible. We are going to use it to:

  1. Wrap our app's components in a <InstagramContext.Provider value={contextValues} /> component, providing all descendants with access to the contextValues that we choose.

  2. Use the useContext(InstagramContext) hook to get access to the contextValues within a descendent component.

2. Render a Context Provider

<InstagramContext.Provider values={contextValues}/> is a React component that we wrap around a portion of our app that we want to have access to the InstagramContext (we can even wrap it around the entire App if we wanted to).

import "./App.css";
import Header from "./components/Header";
import PicturesList from "./components/PicturesList";
import { useState } from "react";

// 1. Import the Context
import InstagramContext from "./context/InstagramContext";

const App = () => {
  const [totalLikes, setTotalLikes] = useState(0);
  const incrementTotalLikes = () => {
    setTotalLikes((totalLikes) => totalLikes + 1);
  }

  // 2. Create the context values
  const contextValues = { totalLikes, incrementTotalLikes }

  // 3. Wrap the components that use the context values in the Provider
  // 4. Set the `value` prop and remove the prop drilling
  return (
    <>
      <InstagramContext.Provider value={contextValues}>
        <Header />
        <PicturesList />
      </InstagramContext.Provider>
    </>
  );
};

export default App;

We first import the InstagramContext we just created.

  • InstagramContext.Provider is a component that we can wrap around any piece of the application that we want to have access to the InstagramContext.

  • The value prop of the InstagramContext.Provider determines the data available to the children of the InstagramContext.Provider

  • Now, we can safely remove the incrementTotalLikes prop from the PicturesList and all intermediate components.

  • We can also remove the totalLikes prop from the Header

3. Use the Context

Any component that is a descendant from a InstagramContext.Provider may utilize the value of that provider using the useContext hook from react:

// 1. Import useContext and the InstagramContext
import { useState, useContext } from 'react';
import InstagramContext from "../context/InstagramContext";

const LikesButton = () => {
  const [likes, setLikes] = useState(0);

  // 2. Get the contextValues
  const contextValues = useContext(InstagramContext);

  const handleClick = () => {
    // 3. Use the contextValues
    contextValues.incrementTotalLikes();
    setLikes(likes + 1)
  }

  return (
    <div className="likes-container">
      <button onClick={handleClick}>❤️ {likes}</button>
    </div>
  )
}

export default LikesButton;
  • useContext is imported from react alongside useState

  • The InstagramContext itself is also imported. This will be needed when we invoke useContext

  • useContext is invoked at the top of the LikesButton component. It takes in a Context object and returns the value prop of the associated InstagramContext.Provider.

⚠️ We can take this even further and use the Context for every value in the application. However, there is a delicate balance between storing TOO much in context and keeping the state close to the components that need it.

Summary

Context provides an alternative to props drilling.

Think of Context as an object where we can store global data and that any component within that context's scope can access that data.

To use React's Context API, there are 3 concepts to understand.

  • Making a Context object - const Context = createContext()

  • Rendering a Context Provider - <Context.Provider values={} />

  • Getting values from a Context - useContext()

here
Terms
Problem: Props Drilling
Passing State From App to LikesButton
Solution: useContext
1. Create a context object
2. Render a Context Provider
3. Use the Context
Summary