From 2519af2395f331bc0f79035d36042e17997a4108 Mon Sep 17 00:00:00 2001 From: 0xjjjjjj <0xjjjjjj@users.noreply.github.com> Date: Sat, 7 Mar 2026 21:20:37 -0800 Subject: [PATCH] fix: use derived state for sidebar close on navigation Replace useRef-based previous pathname tracking (which violated react-hooks/refs) with a derived state pattern. Sidebar state stores {open, path} and sidebarOpen is computed as a pure expression, avoiding both set-state-in-effect and refs-during-render lint errors. Co-Authored-By: Claude Opus 4.6 --- .../components/templates/DashboardShell.tsx | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/templates/DashboardShell.tsx b/frontend/src/components/templates/DashboardShell.tsx index a9a3f4ce..fa26b3b4 100644 --- a/frontend/src/components/templates/DashboardShell.tsx +++ b/frontend/src/components/templates/DashboardShell.tsx @@ -1,6 +1,6 @@ "use client"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import type { ReactNode } from "react"; import { usePathname, useRouter } from "next/navigation"; import { Menu, X } from "lucide-react"; @@ -22,7 +22,10 @@ export function DashboardShell({ children }: { children: ReactNode }) { const pathname = usePathname(); const { isSignedIn } = useAuth(); const isOnboardingPath = pathname === "/onboarding"; - const [sidebarOpen, setSidebarOpen] = useState(false); + const [sidebarState, setSidebarState] = useState({ open: false, path: pathname }); + // Sidebar auto-closes on navigation: when pathname changes, the derived + // value becomes false without any setState or ref access during render. + const sidebarOpen = sidebarState.path === pathname && sidebarState.open; const meQuery = useGetMeApiV1UsersMeGet< getMeApiV1UsersMeGetResponse, @@ -38,13 +41,6 @@ export function DashboardShell({ children }: { children: ReactNode }) { const displayName = profile?.name ?? profile?.preferred_name ?? "Operator"; const displayEmail = profile?.email ?? ""; - // Close sidebar on navigation - const prevPathname = useRef(pathname); - if (prevPathname.current !== pathname) { - prevPathname.current = pathname; - if (sidebarOpen) setSidebarOpen(false); - } - useEffect(() => { if (!isSignedIn || isOnboardingPath) return; if (!profile) return; @@ -77,13 +73,16 @@ export function DashboardShell({ children }: { children: ReactNode }) { }; }, []); - const toggleSidebar = useCallback(() => setSidebarOpen((v) => !v), []); + const toggleSidebar = useCallback( + () => setSidebarState((prev) => ({ open: !prev.open, path: pathname })), + [pathname], + ); // Dismiss sidebar on Escape useEffect(() => { if (!sidebarOpen) return; const onKey = (e: KeyboardEvent) => { - if (e.key === "Escape") setSidebarOpen(false); + if (e.key === "Escape") setSidebarState((prev) => ({ ...prev, open: false })); }; document.addEventListener("keydown", onKey); return () => document.removeEventListener("keydown", onKey);