React中JWT过期未自动跳转登录页问题及解决请求
问题分析
你的代码存在三个核心问题导致JWT过期后无法自动跳转,仅能通过刷新页面触发:
- useEffect仅监听
auth变量变化,当checkLogin修改isLoggedIn状态后,不会触发useEffect执行跳转逻辑 checkLogin中修改isLoggedIn是异步状态更新,useEffect里直接判断isLoggedIn拿到的是初始值,无法正确触发跳转- 缺少定时检查token过期的机制,页面打开期间token过期时不会主动检测
解决方案
步骤1:统一登出逻辑
抽离handleLogout函数,集中处理状态更新、清除token、跳转等操作,避免重复代码。
步骤2:添加定时过期检测
在checkLogin中计算token剩余有效期,设置定时器在token过期时自动触发登出,确保页面打开期间token过期能及时响应。
步骤3:完善副作用依赖与清理
组件挂载时执行一次登录检查,同时添加清理函数在组件卸载时清除定时器,防止内存泄漏。
修改后的完整代码
import React, { useEffect, useState } from 'react'; import { Navigate, useRoutes, useNavigate } from 'react-router-dom'; import JwtDecode from "jwt-decode"; // layouts import DashboardLayout from './layouts/dashboard'; import SimpleLayout from './layouts/simple'; // pages import SignIn from './pages/Auth/SignIn'; import SignUp from './pages/Auth/SignUp'; import Otp from './pages/Auth/Otp'; import Dashboard from './pages/Dashboard/AppAnalytics'; import AlumniListing from './pages/Alumni/AlumniListing'; import Page404 from './pages/Page404'; import Profile from './pages/Profile/index'; import EditProfile from './pages/EditProfile/index'; import Payment from './pages/Auth/Payment'; import PaymentStatus from './pages/Auth/PaymentStatus'; export default function Router() { const [isLoggedIn, setIsLoggedIn] = useState(false); const navigate = useNavigate(); const [expirationTimer, setExpirationTimer] = useState(null); const handleLogout = () => { setIsLoggedIn(false); localStorage.removeItem("token"); if (expirationTimer) clearTimeout(expirationTimer); navigate("/auth/sign-in", { replace: true }); }; const checkLogin = () => { const auth = localStorage.getItem("token"); if (auth) { try { const decodedToken = JwtDecode(auth); const currentTime = Math.floor(new Date().getTime() / 1000); if (currentTime >= decodedToken.exp) { console.log("JWT已过期"); handleLogout(); } else { console.log("JWT有效"); setIsLoggedIn(true); // 设置定时器,在token过期时自动登出 const timeUntilExpire = (decodedToken.exp - currentTime) * 1000; const timer = setTimeout(handleLogout, timeUntilExpire); setExpirationTimer(timer); } } catch (error) { console.error("解码JWT出错:", error); handleLogout(); } } else { setIsLoggedIn(false); } }; useEffect(() => { checkLogin(); // 组件卸载时清理定时器 return () => { if (expirationTimer) clearTimeout(expirationTimer); }; }, []); const routes = useRoutes([ { path: '/dashboard', element: isLoggedIn ? <DashboardLayout /> : <Navigate to='/auth/sign-in' replace />, children: [ { element: <Navigate to="/dashboard/analytics" replace />, index: true }, { path: 'analytics', element: <Dashboard /> }, { path: 'alumni', element: <AlumniListing /> }, { path: 'profile', element: <Profile /> }, { path: 'edit-profile', element: <EditProfile /> }, ], }, { path: 'auth/sign-in', element: !isLoggedIn ? <SignIn /> : <Navigate to='/dashboard/analytics' replace />, }, { path: 'payment', element: <Payment />, }, { path: 'payment/gateway-response', element: <PaymentStatus />, }, { path: 'verify-otp', element: !isLoggedIn ? <Otp /> : <Navigate to='/dashboard/analytics' replace />, }, { path: 'auth/sign-up', element: !isLoggedIn ? <SignUp /> : <Navigate to='/dashboard/analytics' replace />, }, { element: <SimpleLayout />, children: [ { path: '404', element: <Page404 /> }, { path: '*', element: <Navigate to="/404" replace /> }, ], }, { path: '*', element: <Navigate to="/404" replace />, }, ]); return routes; }
关键修改点说明
- 新增
handleLogout函数统一处理登出流程,包含状态更新、清除token、清理定时器和跳转操作 - 在
checkLogin中计算token剩余有效期,设置定时器自动触发登出,实现页面打开期间的过期检测 - useEffect仅在组件挂载时执行一次登录检查,添加清理函数避免定时器内存泄漏
- 路由跳转使用
replace属性,防止历史记录中堆积无效跳转记录
内容的提问来源于stack exchange,提问作者holoyo
相关产品推荐
相关产品推荐

