From dd0700cbea403ed99444e1c5accdeb5df75fcb07 Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Sun, 22 Mar 2026 19:56:00 +0100 Subject: [PATCH] fix(pagespeed): poll silently without triggering SWR re-renders Use direct API fetch for polling instead of mutateLatest() which was causing the page to flicker and clear data every 5 seconds. SWR cache is only updated once when new results arrive. --- app/sites/[id]/pagespeed/page.tsx | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/app/sites/[id]/pagespeed/page.tsx b/app/sites/[id]/pagespeed/page.tsx index c0b0f34..081ae72 100644 --- a/app/sites/[id]/pagespeed/page.tsx +++ b/app/sites/[id]/pagespeed/page.tsx @@ -4,7 +4,7 @@ import { useAuth } from '@/lib/auth/context' import { useEffect, useState, useRef, useCallback } from 'react' import { useParams } from 'next/navigation' import { useSite, usePageSpeedConfig, usePageSpeedLatest, usePageSpeedHistory } from '@/lib/swr/dashboard' -import { updatePageSpeedConfig, triggerPageSpeedCheck, type PageSpeedCheck, type AuditSummary } from '@/lib/api/pagespeed' +import { updatePageSpeedConfig, triggerPageSpeedCheck, getPageSpeedLatest, type PageSpeedCheck, type AuditSummary } from '@/lib/api/pagespeed' import { toast, Button } from '@ciphera-net/ui' import ScoreGauge from '@/components/pagespeed/ScoreGauge' import { @@ -116,7 +116,6 @@ export default function PageSpeedPage() { } // * Trigger a manual PageSpeed check - // * Poll for results after triggering an async check const pollRef = useRef | null>(null) const stopPolling = useCallback(() => { if (pollRef.current) { @@ -125,7 +124,6 @@ export default function PageSpeedPage() { } }, []) - // * Clean up polling on unmount useEffect(() => () => stopPolling(), [stopPolling]) const handleRunCheck = async () => { @@ -134,25 +132,29 @@ export default function PageSpeedPage() { await triggerPageSpeedCheck(siteId) toast.success('PageSpeed check started — results will appear in 30-60 seconds') - // * Poll every 5s for up to 2 minutes until new results appear - const startedAt = Date.now() + // * Poll silently without triggering SWR re-renders. + // * Fetch latest directly and only update SWR cache once when new data arrives. const initialCheckedAt = latestChecks?.[0]?.checked_at + const startedAt = Date.now() stopPolling() pollRef.current = setInterval(async () => { - const elapsed = Date.now() - startedAt - if (elapsed > 120_000) { + if (Date.now() - startedAt > 120_000) { stopPolling() setRunning(false) toast.error('Check is taking longer than expected. Results will appear when ready.') return } - const freshData = await mutateLatest() - const freshCheckedAt = freshData?.[0]?.checked_at - if (freshCheckedAt && freshCheckedAt !== initialCheckedAt) { - stopPolling() - setRunning(false) - toast.success('PageSpeed check complete') + try { + const fresh = await getPageSpeedLatest(siteId) + if (fresh?.[0]?.checked_at && fresh[0].checked_at !== initialCheckedAt) { + stopPolling() + setRunning(false) + mutateLatest() // * Single SWR revalidation when new data is ready + toast.success('PageSpeed check complete') + } + } catch { + // * Silent — keep polling } }, 5000) } catch (err: any) {