From 005c3ea7b50e42f4fbb63f643f1109819fd30fba Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Wed, 22 Apr 2026 09:59:11 +0200 Subject: [PATCH] fix(home): rehydrate-Trigger aus snapshot.restore, nicht ueber onMount MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Live-Test mit Fetch-Hook auf kochwas-dev hat bewiesen: bei tiefem Endless-Scroll (60 Items) und Back-Nav fired rehydrateAll nie. Der Logger zeigte nur limit=10&offset=0 plus IO-Trigger (10&offset=10). Root Cause: SvelteKit ruft snapshot.restore *nach* onMount (post-mount tick). Der vorherige Code parkte die Tiefe in pendingPagination und liess onMount entscheiden — onMount lief aber zuerst, sah null, fiel auf loadAllMore() zurueck. Restore setzte danach pendingPagination, aber niemand las es mehr. Fix: rehydrateAll direkt aus restore aufrufen (fire-and-forget). onMount macht unkonditional loadAllMore() fuer den Fresh-Mount-Fall; beide Pfade greifen ueber das allLoading-Flag bzw. ueber den allRecipes-Overwrite (rehydrateAll's groesseres Result gewinnt spaeter). Wasted-Fetch im Worst-Case: 10 Items (~2 KB) — vertretbar. pendingPagination raus, onMount-Conditional vereinfacht. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/routes/+page.svelte | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 21228b7..f0a7dd9 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -47,12 +47,18 @@ // recipes via infinite scroll") so on back-nav we can re-hydrate the // full list in one fetch — otherwise the document is too short and // scroll-restore can't reach the saved Y position. + // + // SvelteKit calls snapshot.restore AFTER onMount (post-mount tick), + // so a flag-based handoff to onMount won't fire — we trigger + // rehydrateAll directly from restore. onMount still calls + // loadAllMore() for the fresh-mount case; if restore lands first, + // allLoading guards the duplicate fetch, otherwise rehydrateAll's + // larger result simply overwrites loadAllMore's initial 10 items. type HomeSnapshot = SearchSnapshot & { allLoaded: number; allSort: AllSort; allExhausted: boolean; }; - let pendingPagination: { count: number; sort: AllSort; exhausted: boolean } | null = null; export const snapshot: Snapshot = { capture: () => ({ @@ -64,12 +70,8 @@ restore: (s) => { store.restoreSnapshot(s); if (s.allLoaded > 0) { - pendingPagination = { - count: s.allLoaded, - sort: s.allSort, - exhausted: s.allExhausted - }; allSort = s.allSort; + void rehydrateAll(s.allSort, s.allLoaded, s.allExhausted); } } }; @@ -161,20 +163,15 @@ if (urlQ) store.query = urlQ; void loadRecent(); void searchFilterStore.load(); - if (pendingPagination) { - // Back-navigation: restore the full loaded depth in one shot so - // the document is tall enough for scroll-restore to land. Sort - // already came from the snapshot — don't read localStorage here. - const p = pendingPagination; - pendingPagination = null; - void rehydrateAll(p.sort, p.count, p.exhausted); - } else { - const saved = localStorage.getItem('kochwas.allSort'); - if (saved && ['name', 'rating', 'cooked', 'created'].includes(saved)) { - allSort = saved as AllSort; - } - void loadAllMore(); + const saved = localStorage.getItem('kochwas.allSort'); + if (saved && ['name', 'rating', 'cooked', 'created'].includes(saved)) { + allSort = saved as AllSort; } + // Fresh-mount: kick off the initial 10. On back-nav, snapshot.restore + // also fires rehydrateAll(60); if it lands first, allLoading guards + // this; if loadAllMore lands first, rehydrateAll's larger result + // simply overwrites allRecipes once it resolves. + void loadAllMore(); }); // IntersectionObserver an den Sentinel hängen — wenn sichtbar, nachladen.