"use client" import { useEffect, useState } from "react" import { useSession } from "@/lib/auth-client" import type { Profile } from "@/types" interface UserState { user: { id: string; email: string; name?: string | null } | null profile: Profile | null loading: boolean } export function useUser(): UserState { const { data: session, isPending } = useSession() // The fetched profile is stored together with the user id it belongs to, so // "loaded" is DERIVED rather than tracked in a second state variable. That // removes the synchronous setState in the effect body (which caused cascading // renders) and, as a bonus, stops a previous user's profile from flashing // while a new one loads. const [fetched, setFetched] = useState<{ userId: string; profile: Profile | null } | null>(null) const userId = session?.user?.id useEffect(() => { if (!userId) return let active = true fetch("/api/profile") .then((r) => (r.ok ? r.json() : { profile: null })) .then((data) => { if (active) setFetched({ userId, profile: data.profile ?? null }) }) .catch(() => { if (active) setFetched({ userId, profile: null }) }) return () => { active = false } }, [userId]) const isCurrent = !!userId && fetched?.userId === userId return { user: session?.user ? { id: session.user.id, email: session.user.email, name: session.user.name } : null, profile: isCurrent ? (fetched?.profile ?? null) : null, // Signed out: nothing to load. Signed in: loading until this user's profile // has actually come back. loading: isPending || (!!userId && !isCurrent), } }