From a4c34ac35503c96df920cd6fb56367b4d90c55c4 Mon Sep 17 00:00:00 2001 From: Mehfuz Date: Thu, 5 Nov 2026 11:19:25 -0800 Subject: [PATCH] Load the user once per Cloud Models visit useAuth refetched /users/me on every route change because pathname was in its effect's dependencies. The Cloud Models layout stays mounted between Usage and Session History, so each switch fetched the user again, and the new user object made every section load its data a second time: about 7 requests for opening Usage instead of 4. useAuth now loads the user once when it mounts. A server or network error from /users/me no longer sends you to /login; the page keeps the user saved by the last successful load. No token or a 401 still signs you out. --- src/app/hooks/useAuth.ts | 44 +++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/app/hooks/useAuth.ts b/src/app/hooks/useAuth.ts index 720535e..7b22db1 100644 --- a/src/app/hooks/useAuth.ts +++ b/src/app/hooks/useAuth.ts @@ -1,39 +1,51 @@ import { useEffect, useState } from 'react'; -import { useRouter, usePathname } from 'next/navigation'; +import { useRouter } from 'next/navigation'; import { userService } from '@/app/services/user.service'; +/** The user saved by the last successful /users/me, or null. */ +function cachedUser(): any { + try { + const saved = localStorage.getItem('u'); + return saved ? JSON.parse(saved) : null; + } catch { + return null; + } +} + /** - * Custom hook for authentication - * Automatically fetches user and redirects to login or invite-verify if needed + * The signed-in user, loaded once when the component using it mounts. + * + * A layout keeps its hooks while you move between its child routes, so the + * user is not fetched again on every page change. Refetching gave the page a + * new user object each time, and everything keyed on the user loaded again. + * + * No session (no token, or a 401) sends you to /login. A server or network + * error does not: the session is still good, so the page carries on with the + * user saved by the last successful load, and its own requests report errors. + * * @returns [user, setUser] tuple - user object (or null) and setter function */ export function useAuth(): [any, (user: any) => void] { const [user, setUser] = useState(null); const router = useRouter(); - const pathname = usePathname(); useEffect(() => { userService.get().then(u => { if (!u) { router.push('/login'); } else { - // // Check if user has access (accepted OR premium OR paid) - // const hasAccess = userService.checkUserAccess(u); - - // // Don't redirect if already on invite-verify page (to avoid infinite loop) - // if (!hasAccess && pathname !== '/invite-verify') { - // // User doesn't have access - redirect to invite request page - // router.push('/invite-verify'); - // } else { - // setUser(u); - // } setUser(u); } }).catch(error => { console.error('useAuth: Error fetching user:', error); - router.push('/login'); + const saved = cachedUser(); + if (saved) { + setUser(saved); + } else { + router.push('/login'); + } }); - }, [router, pathname]); + }, [router]); return [user, setUser]; }