From 80176cac4db4820e76ea2a156c7a2974bee2f203 Mon Sep 17 00:00:00 2001 From: Tudor Date: Fri, 17 Jul 2026 07:27:10 +0100 Subject: [PATCH] =?UTF-8?q?fix(compare):=20drop=20the=20merge-on-fetch=20?= =?UTF-8?q?=E2=80=94=20cancellation=20alone=20fixes=20the=20race?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review finding: merging left a re-added school's old entry in the map forever, so the covered check served stale data and the map grew unboundedly. The cancelled flag already discards superseded responses, which is the actual race fix; replacing keeps the map bounded to the current selection and guarantees fresh refetches. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB --- nextjs-app/components/ComparisonView.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/nextjs-app/components/ComparisonView.tsx b/nextjs-app/components/ComparisonView.tsx index 5f5b163..b3973af 100644 --- a/nextjs-app/components/ComparisonView.tsx +++ b/nextjs-app/components/ComparisonView.tsx @@ -133,15 +133,16 @@ export function ComparisonView({ // Guard against out-of-order responses: while the basket hydrates from // localStorage it can transiently hold a DIFFERENT school set than the // URL, firing a fetch for schools the user is no longer comparing. That - // stale response must not replace data for the current set — replacing - // it blanked every section until a hard refresh. We (a) drop responses - // from superseded effect runs and (b) merge rather than replace, so data - // for the current schools always survives. + // stale response must not replace data for the current set — it blanked + // every section until a hard refresh. Cleanup marks the run cancelled + // when urnKey moves on, so only the current selection's response is + // applied (replacing the map keeps it bounded and guarantees a re-added + // school is refetched fresh rather than served a lingering old entry). let cancelled = false; fetchComparison(urnKey, { cache: 'no-store' }) .then((data) => { if (cancelled) return; - setComparisonData((prev) => ({ ...(prev ?? {}), ...data.comparison })); + setComparisonData(data.comparison); setNationalAverages(data.national_averages); setBenchmarks(data.benchmarks); })