fix(compare): blank page on refresh + remove dead per-page comparison fetch #37

Merged
tudor merged 1 commits from fix/compare-refresh-and-fetch into main 2026-07-14 21:39:47 +00:00
Owner

Fixes the compare-page refresh bug and removes a redundant per-page comparison fetch.

Bug: blank compare page on refresh

On mount the comparison basket is empty for one render before it hydrates from the URL. The fetch effect nulled comparisonData on that transient empty urnKey, and the one-shot "SSR already covers this set" skip then suppressed the refetch — so a reload showed the SSR-rendered data for a flash, then blanked.

Fix: the fetch effect is gated on isInitialized, never blanks on an empty set (the render already shows the empty state when nothing is selected), and decides fetch-vs-skip by whether it already holds each requested school's data (from SSR or a prior fetch). Bonus: removing a school no longer triggers a refetch.

A regression test reproduces the refresh scenario (SSR data + basket hydrating from empty) and fails against the old code.

Perf: dead per-page /api/compare fetch removed

useComparison() ran a useSWR('/api/compare?urns=…') whose result nothing consumed. Because Navigation and ComparisonToast mount it globally, that fetch fired on every page whenever the basket was non-empty — and duplicated ComparisonView's own fetch on the compare page itself. The hook now exposes basket state only.

Tests

78 frontend tests (1 new, verified to fail on the pre-fix code) · tsc clean.

Measured (staging)

/api/national-averages (the PR #36 mart fix) is now ~60ms (was ~250ms). The remaining /api/compare cost scales ~37ms per school — the per-URN supplementary DB round-trips — see the follow-up note in the PR discussion.

🤖 Generated with Claude Code

https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB

Fixes the compare-page refresh bug and removes a redundant per-page comparison fetch. ## Bug: blank compare page on refresh On mount the comparison basket is empty for one render before it hydrates from the URL. The fetch effect nulled `comparisonData` on that transient empty `urnKey`, and the one-shot "SSR already covers this set" skip then suppressed the refetch — so a reload showed the SSR-rendered data for a flash, then blanked. Fix: the fetch effect is gated on `isInitialized`, never blanks on an empty set (the render already shows the empty state when nothing is selected), and decides fetch-vs-skip by whether it already holds each requested school's data (from SSR or a prior fetch). Bonus: removing a school no longer triggers a refetch. A regression test reproduces the refresh scenario (SSR data + basket hydrating from empty) and fails against the old code. ## Perf: dead per-page `/api/compare` fetch removed `useComparison()` ran a `useSWR('/api/compare?urns=…')` whose result **nothing consumed**. Because `Navigation` and `ComparisonToast` mount it globally, that fetch fired on *every* page whenever the basket was non-empty — and duplicated `ComparisonView`'s own fetch on the compare page itself. The hook now exposes basket state only. ## Tests 78 frontend tests (1 new, verified to fail on the pre-fix code) · tsc clean. ## Measured (staging) `/api/national-averages` (the PR #36 mart fix) is now ~60ms (was ~250ms). The remaining `/api/compare` cost scales ~37ms per school — the per-URN supplementary DB round-trips — see the follow-up note in the PR discussion. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB
tudor added 1 commit 2026-07-14 21:35:50 +00:00
fix(compare): show SSR data on refresh; drop dead per-page comparison fetch
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 9m42s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 10s
PR Checks / Build Frontend (no push) (pull_request) Successful in 49s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 9s
43a2c4a6bc
Refresh bug: on mount the basket is empty for a beat before it hydrates
from the URL. The fetch effect nulled comparisonData on that transient
empty urnKey, then the one-shot 'SSR covers it' skip suppressed the
refetch — leaving the page blank on reload. The effect is now gated on
isInitialized, never blanks on empty (the render already shows the empty
state when nothing is selected), and decides fetch-vs-skip by whether it
already holds each requested school's data (SSR or a prior fetch).

Perf: useComparison ran a useSWR('/api/compare') whose result nothing
consumed — dead weight that fired on every page (Navigation + Toast are
global) whenever the basket was non-empty, and duplicated ComparisonView's
own fetch on the compare page. Removed; the hook now exposes basket state
only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB
tudor merged commit abc03a0dd3 into main 2026-07-14 21:39:47 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: tudor/school_compare#37