React路由保护后登录成功仍跳转回登录页的问题求助
登录后跳转受保护路由立即返回登录页的问题排查与修复
我用<ProtectedRoutes/>组件做路由保护,预期用户登录成功后自动跳转到/dashboard,但现在不管登录多少次,URL会短暂切到/dashboard,马上又跳回/login。已经确认能从Supabase拿到完整的认证用户对象。
路由配置
const router = createBrowserRouter([ { element: <ProtectedRoutes/>, children: [ { element: <AppLayouts />, children: [ { path: "/", element: <Navigate to="/dashboard" replace />, }, { path: "/dashboard", element: <Dashboard />, }, { path: "/users", element: <NewUsers />, }, { path: "/account", element: <Account />, }, { path: "/bookings", element: <Bookings />, }, { path: "/bookings/:id", element: <OneBookingDetails/>, }, { path: "/checkin/:id", element: <Checkin/>, }, { path: "/cabins", element: <Cabins />, }, { path: "/settings", element: <Settings />, }, ], }, ] }, { path: "/login", element: <Login />, }, { path: "*", element: <PageNotFound />, }, ]);
ProtectedRoutes组件
export default function ProtectedRoutes({children}) { const navigate = useNavigate(); // 加载认证用户 const {isAuthenticated, isLoading} = useUser(); useEffect(function(){ // 未认证且加载完成时跳转到登录页 if(!isAuthenticated && !isLoading) navigate("/login"); }, [isAuthenticated, isLoading, navigate]); // 加载中显示加载动画 if(isLoading) return <SpinnerContainer><Spinner/></SpinnerContainer> // 已认证则渲染子组件 if(isAuthenticated) return children; }
useUser钩子
import { useQuery } from "@tanstack/react-query"; import { getCurrentUser } from "../../services/apiAuth"; export function useUser() { const {isLoading, data: user, fetchStatus} = useQuery({ queryKey: ["user"], queryFn: getCurrentUser }); return {isLoading, fetchStatus, user, isAuthenticated: user?.role === "authenticated"}; }
useLogin钩子
import { useMutation } from "@tanstack/react-query"; import { login as loginApi } from "../../services/apiAuth"; import { useNavigate } from "react-router-dom"; import toast from "react-hot-toast"; export function useLogin() { const navigate = useNavigate(); const {isLoading: isLogging, mutate: login} = useMutation({ mutationFn: ({email, password}) => loginApi({email, password}), onSuccess: () => { toast.success("Welcome Back!"); navigate("/dashboard"); }, onError: (error) => { console.log(error.message); toast.error("something went wrong, pleast try to login again.") } }); return {isLogging, login} }
排查信息
- 在
ProtectedRoutes的useEffect()中打印user为undefined,但useUser()内部的user不为空 - 移除
useEffect中的跳转逻辑后,能成功跳转到/dashboard但页面空白,其他受保护路由也一样
问题根源分析
- React Query缓存未及时更新:登录成功后,
useUser钩子中的useQuery还持有旧的缓存(user为undefined),导致isAuthenticated为false,触发useEffect中的跳转逻辑。 - ProtectedRoutes组件缺少兜底返回:当
isAuthenticated为false且isLoading也为false时,组件没有返回任何内容,这就是移除跳转逻辑后页面空白的原因。
修复步骤
1. 登录成功后手动刷新用户查询缓存
修改useLogin钩子的onSuccess回调,调用React Query的queryClient.invalidateQueries强制刷新user查询,确保useUser能拿到最新的认证状态:
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { login as loginApi } from "../../services/apiAuth"; import { useNavigate } from "react-router-dom"; import toast from "react-hot-toast"; export function useLogin() { const navigate = useNavigate(); const queryClient = useQueryClient(); const {isLoading: isLogging, mutate: login} = useMutation({ mutationFn: ({email, password}) => loginApi({email, password}), onSuccess: () => { toast.success("欢迎回来!"); // 刷新user查询缓存 queryClient.invalidateQueries({ queryKey: ["user"] }); navigate("/dashboard"); }, onError: (error) => { console.log(error.message); toast.error("登录失败,请重试。") } }); return {isLogging, login} }
2. 完善ProtectedRoutes组件的兜底逻辑
移除useEffect中的跳转逻辑,改为在组件渲染阶段直接判断,同时确保所有分支都有返回值:
export default function ProtectedRoutes({children}) { const navigate = useNavigate(); const {isAuthenticated, isLoading} = useUser(); // 加载中显示加载动画 if(isLoading) return <SpinnerContainer><Spinner/></SpinnerContainer> // 未认证则跳转到登录页 if(!isAuthenticated) { navigate("/login"); return null; } // 已认证则渲染子组件 return children; }
3. 确认getCurrentUser的返回值格式
确保getCurrentUser函数返回的用户对象包含role字段且值为"authenticated",示例实现:
export async function getCurrentUser() { const { data } = await supabase.auth.getUser(); return data.user; // 确保返回的user对象包含role属性 }
修复原理
- 登录成功后主动刷新
user查询缓存,让useUser立即拿到最新的认证用户数据,isAuthenticated变为true,避免ProtectedRoutes误判用户未认证。 - 移除
useEffect的跳转逻辑,改为在组件渲染阶段直接判断,避免异步更新带来的状态不一致问题。同时给未认证分支加上返回值,解决页面空白问题。
内容的提问来源于stack exchange,提问作者Marya
相关产品推荐
相关产品推荐

