Good Code
The good version memoizes a filtered list and lists the inputs that affect that list.
Lesson 09
Use memoization for repeated expensive work with correct dependencies, not as decoration.
import { useMemo } from "react";
type Review = {
id: string;
title: string;
authorName: string;
};
function matchesSearch(review: Review, search: string) {
const query = search.toLowerCase();
return (
review.title.toLowerCase().includes(query) ||
review.authorName.toLowerCase().includes(query)
);
}
export function ReviewSearch({
reviews,
search,
}: {
reviews: Review[];
search: string;
}) {
// Memoization is tied to the expensive list calculation inputs.
const visibleReviews = useMemo(
() => reviews.filter((review) => matchesSearch(review, search)),
[reviews, search],
);
return (
<ul>
{visibleReviews.map((review) => (
<li key={review.id}>{review.title}</li>
))}
</ul>
);
}import { useMemo } from "react";
type Review = {
id: string;
title: string;
authorName: string;
};
function matchesSearch(review: Review, search: string) {
return review.title.toLowerCase().includes(search.toLowerCase());
}
export function ReviewSearch({
reviews,
search,
}: {
reviews: Review[];
search: string;
}) {
// Empty dependencies freeze the list after the first render.
const visibleReviews = useMemo(
() => reviews.filter((review) => matchesSearch(review, search)),
[],
);
const resultLabel = useMemo(
() => "Showing " + visibleReviews.length + " reviews",
[visibleReviews.length],
);
return (
<>
<p>{resultLabel}</p>
<ul>
{visibleReviews.map((review) => (
<li key={review.id}>{review.title}</li>
))}
</ul>
</>
);
}The good version memoizes a filtered list and lists the inputs that affect that list.
The bad version memoizes the list with an empty dependency array, so the UI can ignore new reviews or search text. It also memoizes a cheap label that does not need caching.