使用Supabase实现Next.js路由保护时出现逻辑异常求助
问题分析与修复方案
你的Protect组件核心问题在于错误处理Supabase异步方法,同时受保护页面存在重复且错误的登录判断逻辑,导致已登录用户被错误重定向。
核心问题点
- Supabase异步方法误用:
supabase.auth.getSession()和supabase.auth.getUser()都是异步函数,直接解构会得到Promise对象而非实际用户/会话数据,导致user始终为undefined,触发重定向逻辑。 - 冗余状态与依赖:
authenticatedState状态未实际使用,useEffect依赖中的auth、isauth是无效的,因为它们并非异步请求的结果。 - 受保护页面重复判断:页面内的
useEffect同样错误调用异步的getUser(),且未处理异步结果,会触发不必要的重定向。
修复后的Protect组件
推荐结合Supabase的状态监听和异步初始化获取用户信息,确保登录状态判断准确:
import { useRouter } from 'next/router' import React, { useEffect, useState } from 'react' import { supabase } from 'src/db/supabase' const Protect = ({ children }) => { const [isLoading, setIsLoading] = useState(true) const [user, setUser] = useState(null) const router = useRouter() useEffect(() => { // 监听登录状态变化,实时更新用户信息 const unsubscribe = supabase.auth.onAuthStateChange((_event, session) => { setUser(session?.user ?? null) setIsLoading(false) }) // 初始化获取当前用户 const fetchCurrentUser = async () => { const { data: { user }, error } = await supabase.auth.getUser() if (!error) { setUser(user) } setIsLoading(false) } fetchCurrentUser() // 组件卸载时取消监听 return () => unsubscribe() }, [router]) useEffect(() => { if (!isLoading && !user) { router.replace('/auth/login') } }, [isLoading, user, router]) // 加载状态返回占位内容,避免闪现受保护页面 if (isLoading) { return <div>加载中...</div> } return <>{children}</> } export default Protect
受保护页面优化
移除页面内重复的登录判断逻辑,完全依赖Protect组件处理路由保护:
import Head from 'next/head'; import { subDays, subHours } from 'date-fns'; import { Box, Container, Unstable_Grid2 as Grid } from '@mui/material'; import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout'; import { OverviewBudget } from 'src/sections/overview/overview-budget'; import { OverviewLatestOrders } from 'src/sections/overview/overview-latest-orders'; import { OverviewLatestProducts } from 'src/sections/overview/overview-latest-products'; import { OverviewSales } from 'src/sections/overview/overview-sales'; import { OverviewTasksProgress } from 'src/sections/overview/overview-tasks-progress'; import { OverviewTotalCustomers } from 'src/sections/overview/overview-total-customers'; import { OverviewTotalProfit } from 'src/sections/overview/overview-total-profit'; import { OverviewTraffic } from 'src/sections/overview/overview-traffic'; import Protect from 'src/contexts/Protect'; const now = new Date(); const Page = () => { return( <Protect> <Box component="main" sx={{ flexGrow: 1, py: 8 }} > <Container maxWidth="xl"> <Grid container spacing={3} > <Grid xs={12} sm={6} lg={3} > <OverviewBudget difference={12} positive sx={{ height: '100%' }} value="$24k" /> </Grid> <Grid xs={12} sm={6} lg={3} > <OverviewTotalCustomers difference={16} positive={false} sx={{ height: '100%' }} value="1.6k" /> </Grid> <Grid xs={12} sm={6} lg={3} > <OverviewTasksProgress sx={{ height: '100%' }} value={75.5} /> </Grid> <Grid xs={12} sm={6} lg={3} > <OverviewTotalProfit sx={{ height: '100%' }} value="$15k" /> </Grid> <Grid xs={12} lg={8} > <OverviewSales chartSeries={[ { name: 'This year', data: [18, 16, 5, 8, 3, 14, 14, 16, 17, 19, 18, 20] }, { name: 'Last year', data: [12, 11, 4, 6, 2, 9, 9, 10, 11, 12, 13, 13] } ]} sx={{ height: '100%' }} /> </Grid> <Grid xs={12} md={6} lg={4} > <OverviewTraffic chartSeries={[63, 15, 22]} labels={['Desktop', 'Tablet', 'Phone']} sx={{ height: '100%' }} /> </Grid> <Grid xs={12} md={6} lg={4} > <OverviewLatestProducts products={[ { id: '5ece2c077e39da27658aa8a9', image: '/assets/products/product-1.png', name: 'Healthcare Erbology', updatedAt: subHours(now, 6).getTime() }, { id: '5ece2c0d16f70bff2cf86cd8', image: '/assets/products/product-2.png', name: 'Makeup Lancome Rouge', updatedAt: subDays(subHours(now, 8), 2).getTime() }, { id: 'b393ce1b09c1254c3a92c827', image: '/assets/products/product-5.png', name: 'Skincare Soja CO', updatedAt: subDays(subHours(now, 1), 1).getTime() }, { id: 'a6ede15670da63f49f752c89', image: '/assets/products/product-6.png', name: 'Makeup Lipstick', updatedAt: subDays(subHours(now, 3), 3).getTime() }, { id: 'bcad5524fe3a2f8f8620ceda', image: '/assets/products/product-7.png', name: 'Healthcare Ritual', updatedAt: subDays(subHours(now, 5), 6).getTime() } ]} sx={{ height: '100%' }} /> </Grid> <Grid xs={12} md={12} lg={8} > <OverviewLatestOrders orders={[ { id: 'f69f88012978187a6c12897f', ref: 'DEV1049', amount: 30.5, customer: { name: 'Ekaterina Tankova' }, createdAt: 1555016400000, status: 'pending' }, { id: '9eaa1c7dd4433f413c308ce2', ref: 'DEV1048', amount: 25.1, customer: { name: 'Cao Yu' }, createdAt: 1555016400000, status: 'delivered' }, { id: '01a5230c811bd04996ce7c13', ref: 'DEV1047', amount: 10.99, customer: { name: 'Alexa Richardson' }, createdAt: 1554930000000, status: 'refunded' }, ]} sx={{ height: '100%' }} /> </Grid> </Grid> </Container> </Box> </Protect> ) }; Page.getLayout = (page) => ( <DashboardLayout> {page} </DashboardLayout> ); export default Page;
额外说明
如果你的useUser上下文是正确实现的,也可以直接使用它替代手动获取用户,代码会更简洁:
// 取消注释这一行即可 const {user,isLoading} = useUser()
内容的提问来源于stack exchange,提问作者Revaycolizer
相关产品推荐
相关产品推荐

