TIER FORGE IS ONLINE: CONSTRUCT AND VISUALIZE RANKED DATA SETS WITH DRAG-AND-DROP PRECISION. ACCESS AT /APPS/TIER-FORGE.

See Tier Forge
Back to SeriesSOURCE: dev

React Context Usecontext

008 - React Context API and useContext

The React Context API provides a way to pass data through the component tree without having to pass props down manually at every level. This is particularly useful for global data (like user authentication, theme, or in this case, toast notifications) that many components might need access to.

The Problem Context Solves (Prop Drilling)

Imagine you have a deeply nested component tree, and a piece of data (e.g., a user object) is needed by a component several levels down. Without Context, you'd have to pass that data as a prop through every intermediate component, even if those components don't directly use the data. This is known as "prop drilling" and can make your code verbose and harder to maintain.

How Context API Works

The Context API consists of three main parts:

  1. createContext: Creates a Context object. When React renders a component that subscribes to this Context object, it will read the current context value from the closest matching Provider above it in the tree.
  2. Provider: A React component that allows consuming components to subscribe to context changes. It accepts a value prop to be passed to consuming components that are descendants of this Provider.
  3. useContext: A React Hook that lets you read context from a functional component.

Example: Toast Notification System

This project uses the Context API to manage and display toast notifications globally. Let's examine src/components/ToastContext.js and src/hooks/useToast.js.

src/components/ToastContext.js (The Provider)

DATA_NODE: javascript
import React, { createContext, useState, useCallback } from 'react'; import Toast from './Toast'; export const ToastContext = createContext(); let id = 0; // Simple counter for unique toast IDs export const ToastContext = ({ children }) => { const [toasts, setToasts] = useState([]); // State to hold active toasts const addToast = useCallback((toast) => { const newToast = { ...toast, id: id++ }; setToasts((prevToasts) => { if (prevToasts.length >= 5) { // Limit to 5 toasts const updatedToasts = prevToasts.slice(0, prevToasts.length - 1); return [newToast, ...updatedToasts]; } return [newToast, ...prevToasts]; }); }, []); // Memoize addToast function const removeToast = useCallback((id) => { setToasts((prevToasts) => prevToasts.filter((toast) => toast.id !== id)); }, []); // Memoize removeToast function return ( <ToastContext.Provider value={{ addToast, removeToast }}> {children} <div className="fixed top-28 right-10 z-50"> {toasts.map((toast) => ( <Toast key={toast.id} id={toast.id} title={toast.title} message={toast.message} duration={toast.duration} removeToast={removeToast} /> ))} </div> </ToastContext.Provider> ); };

Explanation:

  1. export const ToastContext = createContext();: A Context object named ToastContext is created. This object will be used by both the Provider and the Consumer.
  2. ToastContext Component: This is a functional component that will wrap parts of your application (as seen in App.js).
    • const [toasts, setToasts] = useState([]);: Manages the array of active toast notifications using useState.
    • addToast and removeToast functions: These functions are responsible for adding new toasts to the toasts array and removing them. They are wrapped in useCallback to prevent unnecessary re-creations, which is an optimization for performance.
    • <ToastContext.Provider value={{ addToast, removeToast }}>: This is the core of the Provider. It makes the addToast and removeToast functions available to any component that consumes ToastContext and is rendered within this Provider's tree. The value prop is crucial here.
    • {children}: This renders whatever components are passed as children to the ToastContext. These children (and their descendants) will have access to the context value.
    • Toast Rendering: The ToastContext also directly renders the actual Toast components based on the toasts state, positioning them in the top-right corner of the screen.

src/hooks/useToast.js (The Consumer Hook)

DATA_NODE: javascript
import { useContext } from 'react'; import { ToastContext } from '../components/ToastContext'; export const useToast = () => { return useContext(ToastContext); };

Explanation:

  1. import { useContext } from 'react';: Imports the useContext Hook from React.
  2. import { ToastContext } from '../components/ToastContext';: Imports the ToastContext object that was created in ToastContext.js.
  3. export const useToast = () => { ... };: This is a custom hook. Custom hooks are a powerful feature in React that allow you to extract reusable stateful logic from components. This useToast hook simplifies consuming the ToastContext.
  4. return useContext(ToastContext);: This line is where the magic happens. When useContext(ToastContext) is called, React looks up the component tree for the closest ToastContext.Provider and returns its value prop. In this case, it returns { addToast, removeToast }.

How it's Used in a Component (e.g., BlogPostPage.js)

DATA_NODE: javascript
// Inside BlogPostPage.js (or any other component that needs toasts) import { useToast } from '../hooks/useToast'; const CodeBlock = ({ /* ... */ }) => { const { addToast } = useToast(); // Access addToast function const handleCopy = () => { // ... copy logic ... addToast({ title: 'Success', message: 'Copied to clipboard!', duration: 3000, }); // ... }; // ... };

Any component that needs to display a toast simply imports and calls useToast(), and it immediately gets access to the addToast function without needing to receive it as a prop from its parent.

Summary

The React Context API, combined with the useContext hook, provides an elegant solution for managing global state and sharing functions across your component tree, avoiding prop drilling and making your application's architecture cleaner and more maintainable. The toast notification system in this project is a prime example of its effective use.