Skip to content

Dataset tab switches render instantly from a shared curated cache - #157

Merged
aperson30 merged 2 commits into
mainfrom
perf/dataset-tab-instant
Aug 15, 2026
Merged

Dataset tab switches render instantly from a shared curated cache#157
aperson30 merged 2 commits into
mainfrom
perf/dataset-tab-instant

Conversation

@aperson30

Copy link
Copy Markdown
Collaborator

Why

Team/Overview -> Dataset tab switches paid a full network round trip + loading-spinner flash every time, even though the curated grid is deterministic. Two causes:

  1. /api/search sends no Cache-Control, so the browser never reuses the request.
  2. Homepage is a plain top-level route — React fully unmounts/remounts it on tab switch, so useDashboard's mount effect re-fetches from scratch every time.

The existing idle warm-up (prefetchCurated) already fetched the same curated data moments after app boot, but only used it to preload preview images and threw the JSON away — so it did nothing to prevent the refetch.

What changed

  • helpers/curatedCache.ts (new): a module-scope cache that is the single source of truth for the curated (tumor/no-tumor, sort_by=quality) fetch. Both the app-boot warm-up and useDashboard's mount effect read/write it, de-duped via an in-flight promise.
  • useDashboard: seeds previewIds/previewMetadata/loading from the cache via a lazy useState initializer when present, so a warm tab switch renders the grid on the very first paint — no skeleton, no request. Cold loads (first visit before warm-up settles, or a genuine fetch failure) fall back to the previous fetch-and-wait behavior unchanged. A total-failure response is not cached, so a transient network blip doesn't wedge the grid empty for the rest of the session.
  • App.tsx: swapped the old prefetchCurated import for warmCuratedCache, same idle-callback timing.
  • Removed helpers/prefetchCurated.ts (superseded).

Filtered/deep-linked searches are unaffected — only the no-filter curated view is cacheable.

Verified

  • npm run typecheck and npm run build both clean (run in a scratch clone on bdmap1, not the prod checkout).

/api/search sends no Cache-Control, so the browser never reuses the
request, and Homepage is a plain top-level route that fully unmounts on
tab switch. Every Team/Overview -> Dataset switch paid a fresh network
round trip plus a loading-spinner flash, even though the curated grid is
deterministic and the existing idle warm-up had just fetched the same
data moments earlier and thrown the result away (it only used it to
preload images).

Add helpers/curatedCache.ts as the single source of truth for that
fetch: a module-scope cache both the app-boot warm-up and useDashboard's
mount effect read/write, de-duped via an in-flight promise. useDashboard
now seeds its initial state from the cache via a lazy useState
initializer when present, so a warm tab switch renders the grid on the
very first paint with no skeleton and no request. Cold loads (first
visit before the warm-up settles, or a real fetch failure) fall back to
the previous fetch-and-wait behavior unchanged.
@aperson30
aperson30 merged commit 967914e into main Aug 15, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant