Context
by Tomas Trescakยท Advanced React

Root Folder
Not Attempted
NameProgress
Introduction
Not Read
๐Ÿฐ Player
Not Read
Setup
Not Attempted
NameProgress
Software Installation
Not Read
Project Setup
Not Read
Running and Testing
Not Read
React and ReactDOM
Not Read
๐Ÿ’ก Assignment 1: Welcome Message
Not Attempted
Submissions
Not Read
React
Not Attempted
NameProgress
JSX and Components
Not Read
Props
Not Read
๐Ÿ‘พ Exercise: Props
Not Attempted
CSS Styles
Not Read
useState and Hooks
Not Read
๐Ÿ‘พ Exercise: useState
Not Attempted
Conditional Rendering
Not Read
Lists
Not Read
๐Ÿ‘พ Exercise: Lists
Not Attempted
Forms and Events
Not Read
๐Ÿ‘พ Exercise: Forms
Not Attempted
๐Ÿ’ก Assignment 2: Front End
Not Attempted
Pure Components - memo
Lifecycle - useEffect
Expensive? - useMemo
DOM Interactions - useRef
forwardRef
useImperativeHandle
๐Ÿ‘พ useImperativeHandle
Not Attempted
Context
useCallback
useId
useReducer
Infrastructure
Not Attempted
NameProgress
Database
Not Read
NextAuth and Github Authentication
Not Read
Prisma and ORM
Not Read
Project Setup
Not Read
Project Authentication
Not Read
APIs
Not Attempted
NameProgress
APIs
Not Read
APIs - Slides
Not Attempted
Rest APIs
Not Read
Rest APIs - Express.js
Not Read
ReastAPIs - Next.js
Not Read
Securing APIs
Not Read
Securing APIs - NextAuth
Not Read
tRPC
Not Attempted
NameProgress
tRPC
Not Read
tRPC - Routers
Not Read
tRPC - Server Rendering
Not Read
tRPC - Client Rendering
Not Read
Persisting Data
Not Read
Assignment 3: APIs
Not Read
0 / 300 XP

Lecture Transcript
If you prefer text to video, check out the transcript of the presentation above

Let's take a look at a very convenient functionality in React, which allows you to share a common state or configuration across your whole app without passing values to each component.

React's Context API allows you to share state (or values) across components without prop drilling. Prop drilling happens when you pass props through multiple nested components to get to a child. Imagine the following application where we use logged-in user info to display content

For example, the information on your current user has to be passed to all the component that use the user ...

In our case, the UserDetails has to pass the user to UserInfo or UserPosts components. Imagine a much deeper nesting and the number of props you would have to pass. With Context API, you can avoid this by making data globally accessible to components that need it. Let's simplify our example.

First, let's import createContext and useContext from react.

Then, for type safety, we define the shape of data stored in the context. In our case, we will store a user property with user details.

Then, we create the context, using the createContext function and provide the default value. This value will be used in case you forget to wrap your component with the context provider. Let's do that now!

First, we initialise the user data ...

... and then we add the UserContext provider with the current user value. From now on, we can access the user value in any of the child components at any level of the hierarchy under the UserContext provider.

We access the value using the useContext hook, providing the type of the context we want to access. Note that we no longer need the user prop as we access the user from the context.

Similarly, we access the user from the context in the UserPosts component. Pretty handy right? Let's add some more functionality and simplify our example.

First thing we simplify is the use of the context with a custom hook.

With this custom hook, we no longer need to remember or import the type of the context. We can conveniently export it from a utility class and import it whenever needed.

Next, we will add the login and logout functionality, and add this function into our context, since login will replace the current user and logout will remove the user from the context.

This is our second simplification, where we create a custom component handling our User Context.

First, there is a user state variable holding the current user value.

Then, this component returns a UserContext provider with ...

... the current user or null if user is not logged in ...

... the login functionality, in our case just hard coding the current user.

... the logout functionality removing the current user

... and also making sure we render all children provided to this Context provider.

We also add a new login form that takes advantage of the new login and logout functions and renders either a login or logout button based on whether the user is logged in or out.

Last, we make sure to use our new UserProvider component with a new LoginForm child.

When we run this example, we can see that login in and out of the application works correctly, propagating the user value through context into all child components. This approach is scalable, clean, and ideal for managing global state in React applications.

Description
All the extra information about this section

Understanding Context API in React

React's Context API allows you to share state (or values) across components without prop drilling. Prop drilling happens when you pass props through multiple nested components to get to a child. Imagine the following application where we use logged-in user info to display content:

import React, { render } from 'react';

function UserInfo({ user }: { user: User }) {
  return <span>{user.name} / {user.uid}</span>
}

function UserPosts({ user }: { user?: User }) {
  return <ul>
    {user.posts.map(i => (
       <li key={i.id}>{i.title}</li>
    ))}
  </ul>
}

function UserDetails({ user }) {
  return (
    <>
      <UserInfo user={user} />
      <UserPosts user={user} />
    </>           
  )
}

export default function App() {
  const user = {
      name: "Tomas",
      uid: 1,
      posts: [
        { id: 1, title: "Post 1" },
        { id: 2, title: "Post 2" },
      ]
  }
  return (
    <UserDetails user={user} />
  );
}

render(<App />)

Every time we want to use user in our application, we would have to provide the user as a prop of the child component. You can see that this can quickly become complicated. For example when you are dealing with themes or localisation you do not want to provide theme and language to every component. With Context API, you can avoid this by making data globally accessible to components that need it.


How Context API Works

  1. Create a Context: This acts as the "container" for the shared state.
  2. Provide the Context: Use the Provider to wrap components that need access to the shared state.
  3. Consume the Context: Use useContext to access the shared values in a component.

Simplifying Context with Custom Providers and Hooks

To make using Context simpler and reusable, weโ€™ll create:

  1. A Custom Context Provider to encapsulate logic.
  2. A Custom Hook for accessing context values.

Step 1: Traditional Context Setup

Before simplifying, here's the traditional way to use Context:

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

// Step 1: Create a Context
const UserContext = createContext();

// Step 2: Provide the Context
export const UserProvider = ({ children }) => {
  const [user, setUser] = useState("John Doe");

  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
};

// Step 3: Consume the Context
const UserProfile = () => {
  const { user, setUser } = useContext(UserContext);
  return (
    <div>
      <h1>User: {user}</h1>
      <button onClick={() => setUser("Jane Doe")}>Change User</button>
    </div>
  );
};

export default function App() {
  return (
    <UserProvider>
      <UserProfile />
    </UserProvider>
  );
}

Step 2: Simplifying with a Custom Hook

To avoid calling useContext(UserContext) repeatedly, letโ€™s create a custom hook.


Custom Hook for Consuming Context

We extract the consumption logic into a custom hook:

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

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

// Custom Hook to Access Context
export const useUser = () => {
  const context = useContext(UserContext);
  if (!context) {
    throw new Error("useUser must be used within a UserProvider");
  }
  return context;
};

// Custom Provider Component
export const UserProvider = ({ children }) => {
  const [user, setUser] = useState("John Doe");

  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
};

Updated Component Using the Custom Hook

Hereโ€™s the cleaner way to consume the context:

import React from "react";
import { UserProvider, useUser } from "./UserContext";

// Consuming Context via Custom Hook
const UserProfile = () => {
  const { user, setUser } = useUser();

  return (
    <div>
      <h1>User: {user}</h1>
      <button onClick={() => setUser("Jane Doe")}>Change User</button>
    </div>
  );
};

export default function App() {
  return (
    <UserProvider>
      <UserProfile />
    </UserProvider>
  );
}

Key Improvements with Custom Hooks and Providers

  1. Custom Provider:
    • The UserProvider encapsulates the Provider setup.
    • Simplifies wrapping components with the context.
  2. Custom Hook:
    • Encapsulates the logic for useContext(UserContext).
    • Provides a clean, reusable API for consuming context.
  3. Error Handling:
    • If useUser is called outside UserProvider, it throws an error. This ensures proper usage.

Benefits of This Approach

  • Cleaner Components: No repetitive useContext calls.
  • Reusable Code: Custom hooks can be reused across components.
  • Error Safety: Guards against incorrect usage of the context.

Summary

  1. Use createContext to set up shared state.
  2. Wrap consumers with a custom provider.
  3. Access context using a custom hook to simplify usage.

This approach is scalable, clean, and ideal for managing global state in React applications.

Let me know if you'd like another example or an advanced use case, like combining useReducer with Context! ๐Ÿš€

Maggie

Discuss with Maggie
Use the power of generative AI to interact with course content

Discussion

0 comments
Loading editor ...
Remember to be polite and report any undesirable behaviour

Category

Empty

Labels

Discussion has no labels

1 participant

user avatar

Priority

Notifications

You're not receiving notifications from this thread.
Course Outline