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

使用@auth0/auth0-react的getAccessTokenSilently在useEffect触发无限循环求助

React Router 6 + Auth0 无限循环问题排查

环境依赖

react-router-dom 6.3.0
react 18.2 
auth0-react 2.0.1

问题描述

遵循《React Router 6 实现 React 认证示例》教程开发,此前所有功能正常,但在useEffect()中调用getAccessTokenSilently()时触发了无限循环。直接调用该方法访问自有API无问题,仅在useEffect中调用时出现循环。

可复现代码片段

Auth0ProviderWithNavigate 实现

import { Auth0Provider } from '@auth0/auth0-react';
import React from 'react';
import { useNavigate } from 'react-router-dom';

export const Auth0ProviderWithNavigate = ({ children }) => {
  const navigate = useNavigate();
  const onRedirectCallback = (appState) => {
    navigate(appState?.returnTo || window.location.pathname);
  };

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

  return (
    <Auth0Provider
      domain={domain}
      clientId={clientId}
      authorizationParams={{
        audience: audience,
        redirect_uri: redirectUri
      }}
      onRedirectCallback={onRedirectCallback}
      useRefreshTokens={true}
    >
      {children}
    </Auth0Provider>
  );
};

AuthenticationGuard 路由守卫

import { withAuthenticationRequired } from '@auth0/auth0-react';

export const AuthenticationGuard = ({ component }) => {
  const Component = withAuthenticationRequired(component, {
    onRedirecting: () => (
      <div className="page-layout">
        Loading...
      </div>
    ),
  });

  return <Component />;
};

index.tsx 入口文件

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';
import { Auth0ProviderWithNavigate } from './Auth0Provider';

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
  <React.Fragment>
    <BrowserRouter>
      <Auth0ProviderWithNavigate>
        <App />
      </Auth0ProviderWithNavigate>
    </BrowserRouter>
  </React.Fragment>
);

reportWebVitals();

App.tsx 组件(出现无限打印)

import * as React from 'react';
import { Routes, Route } from 'react-router-dom';
import Home from 'pages/Home';
import { AuthenticationGuard } from 'components/AuthenticationGuard';
import { AppProvider } from './context';
import { useAuth0 } from '@auth0/auth0-react';

export default function App(): JSX.Element {
  const { isAuthenticated } = useAuth0();
  console.log('isAuthenticated: ', isAuthenticated); // 无限循环打印
  return (
    <React.Fragment>
      <AppProvider>
        <div>
          <Routes>
            <Route path="/" element={<AuthenticationGuard component={Home} />} />
          </Routes>
        </div>
      </AppProvider>
    </React.Fragment>
  );
}

AppProvider 上下文

import React, { createContext, useReducer } from 'react';

interface ProviderProps {
  children: React.ReactNode;
}

const AppContext = createContext<any>({});

const combinedReducers = (state: any, action: any) => {
  // 合并的reducer逻辑
  return state;
};

const initialState = {};

const AppProvider = ({ children }: ProviderProps) => {
  const [state, dispatch] = useReducer(combinedReducers, initialState);
  return <AppContext.Provider value={{ state, dispatch }}>{children}</AppContext.Provider>;
};
export { AppProvider };

Home 组件(触发循环的核心)

import * as React from 'react';
import { useEffect } from 'react';
import { useAuth0 } from '@auth0/auth0-react';

export default function Home(): JSX.Element {
  const { getAccessTokenSilently } = useAuth0();
  
  useEffect(() => {
    (async () => {
      const token = await getAccessTokenSilently({
        authorizationParams: {
          audience: audience
        },
      });
      console.log('token ', token); // 无限循环打印
    })();
  }, []);

  return (
    <div>Hello from app!</div>
  );
}

问题原因分析

  1. Auth0状态触发重渲染循环:getAccessTokenSilently调用时,可能触发Auth0内部状态更新(比如token刷新、isAuthenticated状态变更)。App组件直接订阅了isAuthenticated,状态变化会导致App重渲染,进而重新渲染AuthenticationGuard和Home组件,Home的useEffect再次调用getAccessTokenSilently,形成闭环。
  2. useEffect依赖缺失:Home组件的useEffect依赖数组为空,但getAccessTokenSilently或audience的引用可能在每次渲染时变化,导致React认为依赖更新,重复执行useEffect。
  3. Refresh Token配置影响:开启useRefreshTokens={true}后,静默获取token时可能触发token刷新流程,多次更新Auth0内部状态,引发重复重渲染。

解决方向

  1. 优化App组件渲染:移除不必要的isAuthenticated订阅(如仅用于调试的打印),或用React.memo包裹App组件,避免无意义的重渲染。
  2. 完善useEffect依赖:将getAccessTokenSilently和audience加入useEffect的依赖数组,并用useCallback包裹异步函数确保引用稳定:
import { useAuth0 } from '@auth0/auth0-react';
import * as React from 'react';
import { useEffect, useCallback } from 'react';

export default function Home(): JSX.Element {
  const { getAccessTokenSilently } = useAuth0();
  
  const fetchToken = useCallback(async () => {
    const token = await getAccessTokenSilently({
      authorizationParams: {
        audience: audience
      },
    });
    console.log('token ', token);
  }, [getAccessTokenSilently, audience]);

  useEffect(() => {
    fetchToken();
  }, [fetchToken]);

  return (
    <div>Hello from app!</div>
  );
}
  1. 优化路由守卫组件:用React.memo包裹被withAuthenticationRequired包装后的组件,避免认证状态变化时的不必要重渲染:
import { withAuthenticationRequired } from '@auth0/auth0-react';
import React from 'react';

export const AuthenticationGuard = ({ component }) => {
  const Component = React.memo(withAuthenticationRequired(component, {
    onRedirecting: () => (
      <div className="page-layout">
        Loading...
      </div>
    ),
  }));

  return <Component />;
};
  1. 确保Auth0配置参数稳定:确认domain、clientId、audience等参数是常量,而非每次渲染都生成的新值(比如避免从动态状态中获取,或用useMemo缓存),防止Auth0Provider反复初始化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 05:47:02