fix(compare): stale basket fetch no longer blanks a freshly opened comparison
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m2s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 11s
PR Checks / Build Frontend (no push) (pull_request) Successful in 42s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 2m23s
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m2s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 11s
PR Checks / Build Frontend (no push) (pull_request) Successful in 42s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 2m23s
Opening a compare link while localStorage held a different basket raced a fetch for the OLD school set against the URL's SSR data; the stale response replaced comparisonData, so no active school had data and every section (including the trends chart) vanished until a hard refresh. Responses from superseded effect runs are now dropped, successful ones merge instead of replace, and the URL-seed effect re-runs when a client-side navigation changes ?urns=. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB
This commit is contained in:
@@ -85,7 +85,9 @@ export function ComparisonView({
|
||||
replaceSchools(urlSchools);
|
||||
}
|
||||
}
|
||||
}, [isInitialized]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
// Re-seed when a client-side navigation lands on a different ?urns= set
|
||||
// (initialUrns/initialData are new props on the same component instance).
|
||||
}, [isInitialized, initialUrns.join(',')]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const urnKey = selectedSchools.map((s) => s.urn).join(',');
|
||||
|
||||
@@ -128,9 +130,18 @@ export function ComparisonView({
|
||||
const covered = urnKey.split(',').every((urn) => have[urn] != null);
|
||||
if (covered) return;
|
||||
|
||||
// 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.
|
||||
let cancelled = false;
|
||||
fetchComparison(urnKey, { cache: 'no-store' })
|
||||
.then((data) => {
|
||||
setComparisonData(data.comparison);
|
||||
if (cancelled) return;
|
||||
setComparisonData((prev) => ({ ...(prev ?? {}), ...data.comparison }));
|
||||
setNationalAverages(data.national_averages);
|
||||
setBenchmarks(data.benchmarks);
|
||||
})
|
||||
@@ -140,6 +151,9 @@ export function ComparisonView({
|
||||
// destroy a working comparison the user is looking at.
|
||||
console.error('Failed to fetch comparison:', err);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [urnKey, isInitialized]);
|
||||
|
||||
const primarySchools = selectedSchools.filter((school) => {
|
||||
|
||||
Reference in New Issue
Block a user