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.
This commit is contained in:
Usman Baig
2026-03-22 19:56:00 +01:00
parent 8649f37bb9
commit dd0700cbea

View File

@@ -4,7 +4,7 @@ import { useAuth } from '@/lib/auth/context'
import { useEffect, useState, useRef, useCallback } from 'react' import { useEffect, useState, useRef, useCallback } from 'react'
import { useParams } from 'next/navigation' import { useParams } from 'next/navigation'
import { useSite, usePageSpeedConfig, usePageSpeedLatest, usePageSpeedHistory } from '@/lib/swr/dashboard' 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 { toast, Button } from '@ciphera-net/ui'
import ScoreGauge from '@/components/pagespeed/ScoreGauge' import ScoreGauge from '@/components/pagespeed/ScoreGauge'
import { import {
@@ -116,7 +116,6 @@ export default function PageSpeedPage() {
} }
// * Trigger a manual PageSpeed check // * Trigger a manual PageSpeed check
// * Poll for results after triggering an async check
const pollRef = useRef<ReturnType<typeof setInterval> | null>(null) const pollRef = useRef<ReturnType<typeof setInterval> | null>(null)
const stopPolling = useCallback(() => { const stopPolling = useCallback(() => {
if (pollRef.current) { if (pollRef.current) {
@@ -125,7 +124,6 @@ export default function PageSpeedPage() {
} }
}, []) }, [])
// * Clean up polling on unmount
useEffect(() => () => stopPolling(), [stopPolling]) useEffect(() => () => stopPolling(), [stopPolling])
const handleRunCheck = async () => { const handleRunCheck = async () => {
@@ -134,25 +132,29 @@ export default function PageSpeedPage() {
await triggerPageSpeedCheck(siteId) await triggerPageSpeedCheck(siteId)
toast.success('PageSpeed check started — results will appear in 30-60 seconds') toast.success('PageSpeed check started — results will appear in 30-60 seconds')
// * Poll every 5s for up to 2 minutes until new results appear // * Poll silently without triggering SWR re-renders.
const startedAt = Date.now() // * Fetch latest directly and only update SWR cache once when new data arrives.
const initialCheckedAt = latestChecks?.[0]?.checked_at const initialCheckedAt = latestChecks?.[0]?.checked_at
const startedAt = Date.now()
stopPolling() stopPolling()
pollRef.current = setInterval(async () => { pollRef.current = setInterval(async () => {
const elapsed = Date.now() - startedAt if (Date.now() - startedAt > 120_000) {
if (elapsed > 120_000) {
stopPolling() stopPolling()
setRunning(false) setRunning(false)
toast.error('Check is taking longer than expected. Results will appear when ready.') toast.error('Check is taking longer than expected. Results will appear when ready.')
return return
} }
const freshData = await mutateLatest() try {
const freshCheckedAt = freshData?.[0]?.checked_at const fresh = await getPageSpeedLatest(siteId)
if (freshCheckedAt && freshCheckedAt !== initialCheckedAt) { if (fresh?.[0]?.checked_at && fresh[0].checked_at !== initialCheckedAt) {
stopPolling() stopPolling()
setRunning(false) setRunning(false)
toast.success('PageSpeed check complete') mutateLatest() // * Single SWR revalidation when new data is ready
toast.success('PageSpeed check complete')
}
} catch {
// * Silent — keep polling
} }
}, 5000) }, 5000)
} catch (err: any) { } catch (err: any) {