diff --git a/frontend/src/shared/hooks/useLastDashboardId.ts b/frontend/src/shared/hooks/useLastDashboardId.ts index da83eb63..287129b9 100644 --- a/frontend/src/shared/hooks/useLastDashboardId.ts +++ b/frontend/src/shared/hooks/useLastDashboardId.ts @@ -15,17 +15,20 @@ export function useLastDashboardId(): [string | null, (id: string | null) => voi } }); - // Watch URL; update sticky id on /dashboard/:id. Do NOT clear when URL stops matching. + // The id from the CURRENT url, read synchronously in render: a dashboard switch must update dashboardId on the SAME render the route changes. Deferring it to the effect below left a one-frame window where the OLD dashboard was still the active id, so a kept-alive browser card from it flashed onto the new dashboard before parking off-screen (the cross-dashboard bleed). + const routeId = location.pathname.match(/^\/dashboard\/([^/]+)/)?.[1] ?? null; + const effectiveId = routeId ?? lastId; + + // Persist the route id so it stays sticky when the url stops matching a dashboard (settings etc.). Do NOT clear when it stops matching. useEffect(() => { - const match = location.pathname.match(/^\/dashboard\/([^/]+)/); - if (match && match[1] && match[1] !== lastId) { - setLastIdState(match[1]); + if (routeId && routeId !== lastId) { + setLastIdState(routeId); try { - localStorage.setItem(STORAGE_KEY, match[1]); + localStorage.setItem(STORAGE_KEY, routeId); } catch {} - setLastDashboardId(match[1]); + setLastDashboardId(routeId); } - }, [location.pathname, lastId]); + }, [routeId, lastId]); const setLastId = useCallback((id: string | null) => { setLastIdState(id); @@ -39,5 +42,5 @@ export function useLastDashboardId(): [string | null, (id: string | null) => voi setLastDashboardId(id); }, []); - return [lastId, setLastId]; + return [effectiveId, setLastId]; }