如何解决NextJS+Supabase应用登录页面闪现问题?
Supabase + Next.js 仪表盘登录页闪现问题
我正在用Supabase搭配Next.js开发仪表盘,遇到一个问题:用户每次进入应用时,Login页面会先闪现1秒,之后系统完成用户认证才跳转到内容页面。(注:未使用Supabase官方登录组件)
相关代码
Layout.tsx
interface Props { children: React.ReactNode; } export default function Layout({ children }: Props) { const supabaseSession = useSession(); return ( <> <Head> <title>LysiMed</title> </Head> {supabaseSession ? ( <Flex className={inter.className}> <Menu /> <Box flex={1}>{children}</Box> </Flex> ) : ( <Login /> )} </> ); }
_app.tsx
export default function App({ Component, pageProps, }: AppProps<{ initialSession: Session }>) { const [loading, setLoading] = React.useState(false); const [supabase] = useState(() => createBrowserSupabaseClient()); React.useEffect(() => { const start = () => { setLoading(true); }; const end = () => { setLoading(false); }; Router.events.on("routeChangeStart", start); Router.events.on("routeChangeComplete", end); Router.events.on("routeChangeError", end); return () => { Router.events.off("routeChangeStart", start); Router.events.off("routeChangeComplete", end); Router.events.off("routeChangeError", end); }; }, []); return ( <SessionContextProvider supabaseClient={supabase} initialSession={pageProps.initialSession} > <ChakraProvider> {loading ? ( <Loading> <Spinner size="xl" color="green.400" /> </Loading> ) : ( <Layout> <Component {...pageProps} /> </Layout> )} </ChakraProvider> </SessionContextProvider> ); }
package.json
{ "next": "13.0.7", "@supabase/auth-helpers-nextjs": "^0.5.2", "@supabase/auth-helpers-react": "^0.3.1", "@supabase/supabase-js": "^2.2.3" }
你的猜测完全正确:useSession()是异步校验用户凭证的,初始阶段会返回null,导致Layout组件先渲染Login页面,等校验完成拿到有效session后才切换到内容页,从而出现闪现。
最佳解决方案
方案一:服务端预认证(推荐,利用Next.js SSR优势)
在需要认证的页面中,使用Supabase的withPageAuth helper在服务端提前校验用户session,未登录则直接重定向到Login,已登录则把session传递给客户端,这样客户端初始就有有效session,不会出现null状态导致的闪现。
示例(仪表盘页面):
import { getServerSideProps } from 'next'; import { withPageAuth } from '@supabase/auth-helpers-nextjs'; // 服务端预校验session,未登录自动重定向到/login export const getServerSideProps = withPageAuth({ redirectTo: '/login' }); export default function Dashboard() { return <div>仪表盘内容</div>; }
方案二:客户端利用useUser处理加载状态
@supabase/auth-helpers-react提供的useUser钩子自带加载状态,可在Layout中等待认证校验完成后再渲染对应页面,避免直接闪现Login。
修改后的Layout.tsx:
import { useUser } from '@supabase/auth-helpers-react'; import { Loading, Spinner } from './你的组件路径'; // 替换为实际加载组件路径 interface Props { children: React.ReactNode; } export default function Layout({ children }: Props) { const { user, loading } = useUser(); // 认证校验完成前显示加载组件 if (loading) { return ( <Loading> <Spinner size="xl" color="green.400" /> </Loading> ); } return ( <> <Head> <title>LysiMed</title> </Head> {user ? ( <Flex className={inter.className}> <Menu /> <Box flex={1}>{children}</Box> </Flex> ) : ( <Login /> )} </> ); }
方案三:手动监听Auth状态(细粒度控制)
如果需要更灵活的状态管理,可手动监听Supabase的Auth状态变化,自行管理session和加载状态:
修改后的Layout.tsx:
import { useSupabaseClient } from '@supabase/auth-helpers-react'; import { Session } from '@supabase/supabase-js'; import { Loading, Spinner } from './你的组件路径'; interface Props { children: React.ReactNode; } export default function Layout({ children }: Props) { const supabase = useSupabaseClient(); const [session, setSession] = React.useState<Session | null>(null); const [isLoading, setIsLoading] = React.useState(true); React.useEffect(() => { // 初始化获取session const fetchSession = async () => { const { data: { session } } = await supabase.auth.getSession(); setSession(session); setIsLoading(false); }; fetchSession(); // 监听Auth状态变化 const { data: listener } = supabase.auth.onAuthStateChange((_event, newSession) => { setSession(newSession); setIsLoading(false); }); // 组件卸载时取消监听 return () => listener?.unsubscribe(); }, [supabase]); if (isLoading) { return ( <Loading> <Spinner size="xl" color="green.400" /> </Loading> ); } return ( <> <Head> <title>LysiMed</title> </Head> {session ? ( <Flex className={inter.className}> <Menu /> <Box flex={1}>{children}</Box> </Flex> ) : ( <Login /> )} </> ); }
内容的提问来源于stack exchange,提问作者jgsneves
相关产品推荐
相关产品推荐

