React

Lesson 08

Context boundaries

Use context for shared ambient data, not as a shortcut around component boundaries.

Good Code

ReviewCard.tsx
import { createContext, useContext } from "react";

type Viewer = {
  id: string;
  name: string;
  role: "viewer" | "maintainer";
};

const ViewerContext = createContext<Viewer | null>(null);

function useViewer() {
  const viewer = useContext(ViewerContext);

  if (!viewer) {
    throw new Error("ReviewCard must be rendered inside ViewerContext.");
  }

  return viewer;
}

type ReviewCardProps = {
  title: string;
  reviewerName: string;
  status: "approved" | "changes-requested" | "pending";
};

export function ReviewCard({
  title,
  reviewerName,
  status,
}: ReviewCardProps) {
  const viewer = useViewer();
  // Context stays narrow while review data remains explicit props.
  const canApprove = viewer.role === "maintainer";

  return (
    <article>
      <h2>{title}</h2>
      <p>{reviewerName} marked this review as {status}.</p>
      {canApprove ? <button type="button">Approve</button> : null}
    </article>
  );
}

Bad Code

ReviewCard.tsx
import { createContext, useContext } from "react";

type AppState = {
  viewer: { id: string; name: string; role: string };
  currentProject: { id: string; name: string };
  activeReview: { title: string; reviewerName: string; status: string };
  searchQuery: string;
  sidebarOpen: boolean;
};

const AppContext = createContext<AppState | null>(null);

export function ReviewCard() {
  // A broad app context subscribes this card to unrelated state.
  const appState = useContext(AppContext);

  if (!appState) {
    return null;
  }

  return (
    <article>
      <h2>{appState.currentProject.name}: {appState.activeReview.title}</h2>
      <p>
        {appState.activeReview.reviewerName} marked this review as{" "}
        {appState.activeReview.status}.
      </p>
      {appState.viewer.role === "maintainer" ? (
        <button type="button">Approve</button>
      ) : null}
    </article>
  );
}

Review Notes

What to review

Good Code

The good version uses context only for ambient viewer information and keeps review data explicit at the component boundary.

Bad Code

The bad version treats a broad app context as a global bag. The component subscribes to project, review, search, sidebar, and viewer data even though it only needs a small subset.

Takeaways

  • Keep context narrow so components do not subscribe to unrelated app state.