mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 21:32:18 +00:00
Merge work/dose-taken-lag
This commit is contained in:
commit
0471c7cb9b
2 files changed
+65
-8
No files matched your search
@@ -194,6 +194,53 @@ describe("prefetchDashboardSnapshot — hydration-safe promise handoff", () => {
|
||||
expect(fetchMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("a cell that already carries data never parks a response, so a write's refetch reads the server as it is AFTER the write", async () => {
|
||||
// The read-after-write rule for the handoff. A parked response is a copy of
|
||||
// the server from the moment it was requested; handing it to a later
|
||||
// `queryFn` run serves pre-write data to a refetch whose entire purpose is
|
||||
// to see the write. The dashboard reaches `queryFn` again only via the
|
||||
// 120 s poll, a focus refetch, or the invalidation a mutation fires — never
|
||||
// via a remount (`refetchOnMount: false`) — so every consumer of a parked
|
||||
// response is one of those three.
|
||||
let serverBody: Record<string, unknown> = {
|
||||
layout: { widgets: [] },
|
||||
tiles: {},
|
||||
medsToday: { takenToday: 0 },
|
||||
};
|
||||
const fetchMock = vi
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve(envelopeResponse(serverBody)));
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
getQueryDataMock.mockReturnValue({ widgets: [{ id: "existing" }] });
|
||||
|
||||
// The user comes back to the dashboard after a few minutes away. The cell
|
||||
// still holds the pre-write body, so the page paints from it at once.
|
||||
prefetchDashboardSnapshot(
|
||||
fakeQueryClient({
|
||||
data: { tiles: {} },
|
||||
dataUpdatedAt: Date.now() - 5 * 60_000,
|
||||
}),
|
||||
);
|
||||
expect(fetchMock).not.toHaveBeenCalled();
|
||||
|
||||
// They record a dose. The server now reports it taken.
|
||||
serverBody = {
|
||||
layout: { widgets: [] },
|
||||
tiles: {},
|
||||
medsToday: { takenToday: 1 },
|
||||
};
|
||||
|
||||
// The write's invalidation refetches the mounted cell.
|
||||
useDashboardSnapshot();
|
||||
const opts = lastOptsOf(useQueryMock);
|
||||
const snap = await (
|
||||
opts.queryFn as () => Promise<{ medsToday: { takenToday: number } }>
|
||||
)();
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
expect(snap.medsToday.takenToday).toBe(1);
|
||||
});
|
||||
|
||||
it("falls back to a fresh fetch when the preload failed — the mounted cell owns error surfacing", async () => {
|
||||
const layout = { widgets: [] };
|
||||
const fetchMock = vi
|
||||
|
||||
@@ -99,15 +99,25 @@ export function prefetchDashboardSnapshot(queryClient: QueryClient) {
|
||||
}
|
||||
// A fresh handoff is already in flight / parked.
|
||||
if (preloadedSnapshot) return;
|
||||
// Cache already warm (return-to-dashboard within staleTime) — the
|
||||
// mounted cell serves it without ever calling `queryFn`; fetching here
|
||||
// would be a wasted request parked in the slot.
|
||||
// The handoff exists to cut the COLD first-load waterfall, so it is only
|
||||
// ever warranted when no snapshot cell has produced data yet. Once a cell
|
||||
// carries data the page paints from it immediately and there is no waterfall
|
||||
// left to cut — but the parked response would still sit in the slot, because
|
||||
// `refetchOnMount: false` means a remount never calls `queryFn` to consume
|
||||
// it. The next `queryFn` run is then whatever comes along: the 120 s poll, a
|
||||
// focus refetch, or the invalidation that follows the user's own write. All
|
||||
// three exist to fetch data that is NEWER than what the cell holds, and all
|
||||
// three would instead be handed a response whose request was issued BEFORE
|
||||
// the write. That is how a just-recorded dose kept reading as still due
|
||||
// until the following poll cleared it.
|
||||
//
|
||||
// v1.21.3 (b) — the live cell is locale-keyed (`["dashboard","snapshot",
|
||||
// locale]`), but the preloader runs before the locale context is in scope.
|
||||
// Probe ALL `["dashboard","snapshot"]` cells (prefix) and treat the freshest
|
||||
// as the warm-slot signal: if any locale's cell is fresh, skip the preload;
|
||||
// otherwise warm the request through the handoff and let the mounted cell
|
||||
// commit it under whichever locale the user lands on.
|
||||
// Probe ALL `["dashboard","snapshot"]` cells (prefix): if ANY locale's cell
|
||||
// has data, skip the preload; otherwise warm the request through the handoff
|
||||
// and let the mounted cell commit it under whichever locale the user lands
|
||||
// on. A cell that exists but never resolved (an error slot) reports
|
||||
// `dataUpdatedAt === 0` and correctly still warms.
|
||||
const states = queryClient
|
||||
.getQueriesData({ queryKey: queryKeys.dashboardSnapshot() })
|
||||
.map(([key]) => queryClient.getQueryState(key));
|
||||
@@ -115,7 +125,7 @@ export function prefetchDashboardSnapshot(queryClient: QueryClient) {
|
||||
(max, s) => (s ? Math.max(max, s.dataUpdatedAt) : max),
|
||||
0,
|
||||
);
|
||||
if (freshest > 0 && Date.now() - freshest < PRELOAD_MAX_AGE_MS) {
|
||||
if (freshest > 0) {
|
||||
return;
|
||||
}
|
||||
const promise = fetchDashboardSnapshot();
|
||||
|
||||
Reference in new issue
Block a user