From 4be7109a2b92c0029779281f3f3a7ffd184df3ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 4 Oct 2026 21:13:28 +0200 Subject: [PATCH] fix(medications): refetch an invalidated prefetch on mount A hover prefetch excused the mount refetch even after a dose logged elsewhere had invalidated the medication reads, so the page showed the compliance from before the dose. An invalidated entry now always refetches. --- .../__tests__/prefetch-medications.test.ts | 22 +++++++++++++++++++ src/lib/queries/prefetch-medications.ts | 9 +++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/lib/queries/__tests__/prefetch-medications.test.ts b/src/lib/queries/__tests__/prefetch-medications.test.ts index 22ee0f750..dc004b368 100644 --- a/src/lib/queries/__tests__/prefetch-medications.test.ts +++ b/src/lib/queries/__tests__/prefetch-medications.test.ts @@ -125,6 +125,28 @@ describe("refetchMedicationsOnMount", () => { unsubscribe(); }); + it("refetches a prefetched read that was invalidated before the mount", async () => { + // Hover the nav link, then log a dose elsewhere before the page mounts: + // the dose invalidates the medication reads, and the prefetched answer + // no longer holds the compliance the user just changed. + const { client, calls } = setup(); + prefetchMedicationsList(client); + await vi.waitFor(() => expect(calls("/api/medications")).toBe(1)); + await vi.waitFor(() => + expect(client.getQueryState(queryKeys.medications())?.status).toBe( + "success", + ), + ); + await client.invalidateQueries({ queryKey: queryKeys.medications() }); + expect(client.getQueryState(queryKeys.medications())?.isInvalidated).toBe( + true, + ); + + const { refetched, unsubscribe } = await mountList(client); + expect(refetched).toBe(true); + unsubscribe(); + }); + it("still refetches on mount when the cache holds an older read", async () => { const { client } = setup(); const queryFn = vi.fn(async () => []); diff --git a/src/lib/queries/prefetch-medications.ts b/src/lib/queries/prefetch-medications.ts index 6058fc137..d78e63c52 100644 --- a/src/lib/queries/prefetch-medications.ts +++ b/src/lib/queries/prefetch-medications.ts @@ -93,11 +93,18 @@ export function prefetchMedicationsList( * the identical request a second time, a few hundred milliseconds after the * first answer landed, on every visit to the page. A return to the page with * an older cache entry, or a direct load, still refetches on mount. + * + * An invalidated entry always refetches: a dose logged elsewhere in the app + * between the prefetch and the mount invalidates these reads, and the + * prefetched answer is then the stale one. */ export function refetchMedicationsOnMount( read: MedicationsRead, -): (query: { state: { dataUpdatedAt: number } }) => boolean | "always" { +): (query: { + state: { dataUpdatedAt: number; isInvalidated: boolean }; +}) => boolean | "always" { return (query) => { + if (query.state.isInvalidated) return "always"; const startedAt = prefetchStartedAt.get(read); const answeredThisVisit = startedAt !== undefined &&