From 5495fd15002d2f388381dda7752238aa68446402 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sat, 4 Apr 2026 09:25:52 -0400 Subject: [PATCH] fix(knowledge): debounce scroll handler with rAF to prevent duplicate page loads Coalesces rapid scroll events into one check per animation frame so that page++ can only fire once per frame, eliminating the window where multiple events slip through before loading=true is observed. Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/views/KnowledgeView.vue | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/frontend/src/views/KnowledgeView.vue b/frontend/src/views/KnowledgeView.vue index 8ca13a4..0463746 100644 --- a/frontend/src/views/KnowledgeView.vue +++ b/frontend/src/views/KnowledgeView.vue @@ -245,15 +245,21 @@ function formatDate(iso: string): string { // ─── Infinite scroll ────────────────────────────────────────────────────────── const cardGridEl = ref(null); +let scrollRafId: number | null = null; function onGridScroll() { - const el = cardGridEl.value; - if (!el || loading.value || items.value.length >= total.value) return; - const nearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 300; - if (nearBottom) { - page.value++; - fetchItems(); - } + // Debounce via rAF — coalesces rapid scroll events into one check per frame + if (scrollRafId !== null) return; + scrollRafId = requestAnimationFrame(() => { + scrollRafId = null; + const el = cardGridEl.value; + if (!el || loading.value || items.value.length >= total.value) return; + const nearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 300; + if (nearBottom) { + page.value++; + fetchItems(); + } + }); } // ─── Lifecycle ──────────────────────────────────────────────────────────────── @@ -266,6 +272,7 @@ onMounted(() => { onUnmounted(() => { if (searchDebounce) clearTimeout(searchDebounce); + if (scrollRafId !== null) cancelAnimationFrame(scrollRafId); });