React

Lesson 06

Effect dependencies

List every effect dependency and clean up async work when inputs change.

Good Code

ReviewComments.tsx
import { useEffect, useState } from "react";

type Comment = {
  id: string;
  body: string;
};

export function ReviewComments({ reviewId }: { reviewId: string }) {
  const [comments, setComments] = useState<Comment[]>([]);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    // The effect follows reviewId and cancels stale network work.
    const controller = new AbortController();

    async function loadComments() {
      setError(null);
      const response = await fetch("/api/reviews/" + reviewId + "/comments", {
        signal: controller.signal,
      });

      if (!response.ok) {
        setError("Could not load comments.");
        return;
      }

      setComments(await response.json());
    }

    loadComments().catch((error) => {
      if (error.name !== "AbortError") {
        setError("Could not load comments.");
      }
    });

    return () => controller.abort();
  }, [reviewId]);

  if (error) {
    return <p role="alert">{error}</p>;
  }

  return comments.map((comment) => <p key={comment.id}>{comment.body}</p>);
}

Bad Code

ReviewComments.tsx
import { useEffect, useState } from "react";

type Comment = {
  id: string;
  body: string;
};

export function ReviewComments({ reviewId }: { reviewId: string }) {
  const [comments, setComments] = useState<Comment[]>([]);

  useEffect(() => {
    // Empty dependencies freeze the first reviewId this render saw.
    fetch("/api/reviews/" + reviewId + "/comments")
      .then((response) => response.json())
      .then(setComments);
  }, []);

  return comments.map((comment) => <p key={comment.id}>{comment.body}</p>);
}

Review Notes

What to review

Good Code

The good version lists reviewId as a dependency and aborts in-flight work when the component unmounts or the review changes.

Bad Code

The bad version captures the initial reviewId forever, so it can display stale comments when the prop changes.

Takeaways

  • Complete dependency arrays make effects follow the data they read.