import { createContext, useContext, useEffect, useState, useCallback, useRef, ReactNode } from 'react'; import { api } from '../api/client'; import type { User } from '../api/types'; import { clearAllFeedCache } from '../utils/feedCache'; import { clearSessionSnapshots } from '../utils/sessionPageCache'; import { peekAuthSeed } from '../utils/authBoot'; interface AuthCtx { user: User | null; loading: boolean; refresh: () => Promise; logout: () => Promise; } const AuthContext = createContext({ user: null, loading: true, refresh: async () => {}, logout: async () => {}, }); export function AuthProvider({ children }: { children: ReactNode }) { // peek 不清种子,StrictMode 重挂 / initializer 双调仍与 SSR 同构 const [user, setUser] = useState(() => { const s = peekAuthSeed(); return s.hasSeed ? s.user : null; }); const [loading, setLoading] = useState(() => !peekAuthSeed().hasSeed); const refresh = useCallback(async () => { try { const data = await api.me(); setUser(data.user ?? null); } catch { setUser(null); } finally { setLoading(false); } }, []); // SSR +data / 冷启动预热已注入登录态种子(含已确认访客 null)时首屏不重复请求; // 无种子(纯 SPA 且未预热)才首屏拉取。登录/退出均显式更新状态,不依赖 mount 校验。 // 不清空 auth 种子:StrictMode 重挂会再次跑 useState initializer,破坏性 clear 会导致 user 空窗闪动 useEffect(() => { if (peekAuthSeed().hasSeed) return; void refresh(); }, [refresh]); const prevUserId = useRef('init'); useEffect(() => { if (loading) return; const id = user?.id ?? null; if (prevUserId.current === 'init') { prevUserId.current = id; return; } if (prevUserId.current !== id) { prevUserId.current = id; clearSessionSnapshots(); clearAllFeedCache(); } }, [user, loading]); const logout = async () => { await api.logout(); setUser(null); }; return ( {children} ); } export const useAuth = () => useContext(AuthContext);