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

离线时如何处理NextJS中Clerk的Token刷新失败未捕获Promise错误?

解决Next.js App Router + Clerk断网时令牌刷新崩溃问题

以下是几个可行的解决思路,从快速修复到高级优化逐步推进:


1. 全局捕获未捕获Promise错误(快速解决崩溃)

直接拦截Clerk令牌刷新失败的未处理Promise错误,阻止应用崩溃:

// app/components/ClerkErrorBoundary.tsx (客户端组件)
'use client';

import { useEffect } from 'react';

export default function ClerkErrorBoundary() {
  useEffect(() => {
    const handleRejection = (event: PromiseRejectionEvent) => {
      // 精准匹配Clerk令牌刷新错误
      if (event.reason?.message?.includes('ClerkJS: Token refresh failed')) {
        event.preventDefault(); // 阻止错误冒泡导致应用崩溃
        console.warn('网络断开,令牌刷新暂时失败,恢复网络后将自动重试');
        // 可选:添加UI提示,告知用户当前网络状态
      }
    };

    window.addEventListener('unhandledrejection', handleRejection);
    return () => window.removeEventListener('unhandledrejection', handleRejection);
  }, []);

  return null;
}

在根layout.tsx中引入该组件(放在ClerkProvider内部或同级)。


2. 结合网络状态监听优化会话处理

通过监听网络在线/离线状态,配合Clerk的会话钩子做针对性处理:

// app/components/SessionOfflineHandler.tsx (客户端组件)
'use client';

import { useAuth } from '@clerk/nextjs';
import { useEffect } from 'react';

export default function SessionOfflineHandler() {
  const { session, signOut } = useAuth();

  useEffect(() => {
    const handleNetworkChange = () => {
      if (!navigator.onLine) {
        // 断网时:标记会话为临时离线状态,避免触发无用的刷新请求
        console.log('网络已断开,将保持本地会话状态');
      } else if (navigator.onLine && session) {
        // 网络恢复时:主动重试刷新会话,失败则引导重新登录
        session.reload()
          .catch(() => {
            console.error('会话已过期,需重新登录');
            signOut();
          });
      }
    };

    window.addEventListener('online', handleNetworkChange);
    window.addEventListener('offline', handleNetworkChange);
    return () => {
      window.removeEventListener('online', handleNetworkChange);
      window.removeEventListener('offline', handleNetworkChange);
    };
  }, [session, signOut]);

  return null;
}

3. 自定义Clerk令牌刷新逻辑(高级优化)

通过Clerk的beforeLoad钩子拦截令牌获取逻辑,断网时返回缓存的有效令牌:

// app/layout.tsx (根布局,客户端部分)
'use client';

import { ClerkProvider } from '@clerk/nextjs';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <ClerkProvider
      beforeLoad={async (clerk) => {
        const originalGetToken = clerk.getToken.bind(clerk);
        clerk.getToken = async (options) => {
          if (!navigator.onLine) {
            // 断网时读取本地缓存的令牌(需提前存储)
            const cachedToken = localStorage.getItem('clerk_cached_token');
            const tokenExpiry = localStorage.getItem('clerk_token_expiry');
            // 校验缓存令牌是否未过期
            if (cachedToken && tokenExpiry && Number(tokenExpiry) > Date.now()) {
              return cachedToken;
            }
            return null;
          }
          // 网络正常时执行原逻辑,并缓存令牌和过期时间
          const token = await originalGetToken(options);
          if (token) {
            // 从令牌解析过期时间(JWT的exp字段)
            const payload = JSON.parse(atob(token.split('.')[1]));
            localStorage.setItem('clerk_cached_token', token);
            localStorage.setItem('clerk_token_expiry', String(payload.exp * 1000));
          }
          return token;
        };
      }}
    >
      {children}
    </ClerkProvider>
  );
}

4. 业务侧降级处理(可选)

如果业务场景允许,在断网且令牌过期时直接触发登出,避免后续错误:

// 可整合到上述SessionOfflineHandler中
if (!navigator.onLine && session && session.isExpired) {
  signOut();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 21:26:14