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

如何让React Router v6 Loader在Auth0完成后再发送请求?

解决React Router Loader与Auth0认证时序冲突问题

问题核心

页面重载时,React Router v6.4的loader会提前于Auth0认证完成执行,导致API请求未携带Bearer token,触发后端权限错误。现有路由结构中,RouterProvider在Auth0 Provider外层,loader执行时Auth0还处于初始化状态,token尚未获取。

解决方案步骤

1. 调整路由初始化时机:Auth0完成加载后再创建路由

将RouterProvider的初始化逻辑移到Auth0认证状态加载完成之后,确保loader执行前Auth0已完成token获取。

修改App.tsx,把路由创建逻辑放到Auth0加载完成的回调中:

import { useState, useEffect } from 'react';
import { Auth0Provider, useAuth0 } from '@auth0/auth0-react';
import { RouterProvider, createBrowserRouter, Router } from 'react-router-dom';
import { ProtectedRoute } from './ProtectedRoute';
import { RawMediasPage } from './RawMediasPage';
import { AuthPage } from './AuthPage';
import { PATHS } from './paths';
import { Loader } from './components/Loader';
import { httpService } from './services/http';

// 定义路由配置模板
const getAppRoutes = () => [
  {
    element: <ProtectedRoute />,
    children: [
      {
        element: <InitApp />,
        children: [
          {
            element: <BaseLayout />,
            children: [
              {
                path: PATHS.rawMedia,
                element: <RawMediasPage />,
                loader: rawMediasLoader,
              },
              // 其他带loader的路由
            ],
          },
        ],
      },
    ],
  },
  {
    path: PATHS.auth,
    element: <AuthPage />,
  },
];

const AppContent = () => {
  const { isLoading, isAuthenticated, getAccessTokenSilently } = useAuth0();
  const [router, setRouter] = useState<Router | null>(null);

  useEffect(() => {
    // 等待Auth0加载完成后初始化路由和token
    if (!isLoading) {
      const initApp = async () => {
        // 已认证用户先获取token,初始化axios拦截器
        if (isAuthenticated) {
          try {
            const token = await getAccessTokenSilently();
            httpService(() => Promise.resolve(token));
          } catch (err) {
            console.error('Token初始化失败:', err);
          }
        }
        // 创建并设置路由
        setRouter(createBrowserRouter(getAppRoutes()));
      };
      initApp();
    }
  }, [isLoading, isAuthenticated, getAccessTokenSilently]);

  // 加载中显示全局加载器
  if (isLoading || !router) {
    return <Loader areaLoader />;
  }

  return <RouterProvider router={router} />;
};

// 根组件包裹Auth0Provider
export const RootApp = () => {
  const domain = process.env.REACT_APP_AUTH0_DOMAIN;
  const clientId = process.env.REACT_APP_AUTH0_CLIENT_ID;
  const audience = process.env.REACT_APP_AUTH0_AUDIENCE;

  if (!(domain && clientId)) {
    return null;
  }

  const onRedirectCallback = (appState: any) => {
    window.location.href = appState?.returnTo || window.location.pathname;
  };

  return (
    <Auth0Provider
      domain={domain}
      clientId={clientId}
      redirectUri={window.location.origin}
      onRedirectCallback={onRedirectCallback}
      audience={audience}
    >
      <AppContent />
    </Auth0Provider>
  );
};

2. 简化ProtectedRoute,移除重复的token获取逻辑

原来的ProtectedRoute中通过useEffect获取token的逻辑可以移除,因为已经在AppContent中完成了初始化:

import { useLocation, Navigate, Outlet } from 'react-router-dom';
import { useAuth0 } from '@auth0/auth0-react';
import { Loader } from './components/Loader';

const ProtectedRoute = () => {
  const location = useLocation();
  const { isAuthenticated, isLoading } = useAuth0();

  if (isLoading) {
    return <Loader areaLoader />;
  }

  return isAuthenticated ? <Outlet /> : <Navigate to={PATHS.auth} state={{ from: location }} replace />;
};

export default ProtectedRoute;

3. 优化Loader:主动获取Token(可选但更可靠)

为了彻底避免依赖拦截器的异步初始化,直接在loader中获取token并携带到请求头:

import { getAuth0 } from '@auth0/auth0-react';
import { LoaderFunction } from 'react-router-dom';

export const rawMediasLoader: LoaderFunction = async () => {
  // 获取Auth0实例,直接调用getAccessTokenSilently
  const auth0 = getAuth0();
  const token = await auth0.getAccessTokenSilently();

  const response = await fetch('/api/raw-medias', {
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
  });

  if (!response.ok) {
    // 抛出错误触发React Router的错误边界
    throw new Response('获取原始媒体失败', { status: response.status });
  }

  return response.json();
};

原理说明

通过将RouterProvider的初始化延迟到Auth0的isLoading状态结束后,确保所有loader执行时,Auth0已完成认证状态的加载,token要么已获取(已认证用户),要么路由会被重定向到登录页(未认证用户)。同时,loader主动获取token的方式,彻底避免了依赖axios拦截器的异步时序问题。

内容的提问来源于stack exchange,提问作者qweezz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 10:25:15