You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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但页面空白,其他受保护路由也一样

问题根源分析

  1. React Query缓存未及时更新:登录成功后,useUser钩子中的useQuery还持有旧的缓存(user为undefined),导致isAuthenticated为false,触发useEffect中的跳转逻辑。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 17:47:35