React Router Dom V6无Token时重定向登录页不生效如何解决
React Router V6 Token不存在/登出时重定向登录页实现
问题根源
判断逻辑不生效核心原因有3个:
- 组件渲染阶段直接调用
navigate()属于违规副作用:React要求渲染过程必须是纯函数,导航、状态更新这类副作用不能直接写在组件函数的同步执行流里,会触发渲染冲突警告,直接导致导航逻辑失效。 - 缺少token状态监听:现有判断只在组件首次加载时执行一次,后续如果token被清除/过期,组件不会重新触发校验逻辑。
- 未覆盖token无效场景:本地存储的token可能是过期、伪造的,就算存在也可能鉴权失败,现有逻辑没有处理接口返回401的情况。
可直接用的实现代码
方式1:单组件修复(快速改现有代码)
把校验逻辑移到useEffect中,同时增加接口鉴权失败的跳转处理:
import React,{useState,useEffect} from 'react'; import axios from 'axios'; import { useNavigate } from 'react-router-dom'; const Home = () => { const [user,setUser]=useState(null); const navigate = useNavigate(); const getUser=async()=>{ // 先判断本地有没有token,没有直接跳 const token = localStorage.getItem('token'); if(!token){ navigate("/login", { replace: true }); return; } try { const res=await axios.get("/auth",{ headers:{ Authorization: `Bearer ${token}`, }, }); setUser(res.data); } catch (err) { // 接口返回401/403说明token无效,清缓存跳登录 if(err.response?.status === 401 || err.response?.status === 403){ localStorage.removeItem("token"); navigate("/login", { replace: true }); } } }; useEffect(()=>{ getUser(); // 监听storage变化,其他标签页登出/清token时当前页也能自动跳转 const handleStorageChange = () => { if(!localStorage.getItem("token")){ navigate("/login", { replace: true }); } } window.addEventListener('storage', handleStorageChange); return () => window.removeEventListener('storage', handleStorageChange); },[navigate]); const logout=()=>{ localStorage.removeItem("token"); // replace: true 替换历史记录,避免用户点返回又回到需要鉴权的页面 navigate("/login", { replace: true }); }; return ( <div className="m-5"> <div className="jumbotron"> <p className="lead">Welcome {user && user.name}</p> <button className="btn btn-danger" onClick={logout}>Logout</button> </div> </div> ) } export default Home
方式2:全局路由守卫(推荐,所有鉴权页面通用)
不用每个页面都重复写校验逻辑,封装一个受保护路由组件,在路由配置层统一拦截:
- 先封装
RequireAuth组件
import { Navigate, useLocation } from 'react-router-dom'; const RequireAuth = ({ children }) => { const location = useLocation(); const token = localStorage.getItem('token'); // 没token直接跳登录,记录当前来源页,登录后可以跳回来 if(!token){ return <Navigate to="/login" state={{ from: location }} replace /> } // 有token直接渲染对应页面 return children; } export default RequireAuth;
- 在路由配置里包裹需要鉴权的页面
import { BrowserRouter, Routes, Route } from 'react-router-dom'; import Home from './Home'; import Login from './Login'; import RequireAuth from './RequireAuth'; const App = () => { return ( <BrowserRouter> <Routes> <Route path="/login" element={<Login />} /> {/* 所有需要登录才能进的页面都包在RequireAuth里 */} <Route path="/" element={ <RequireAuth> <Home /> </RequireAuth> } /> {/* 其他受保护路由照这个格式加即可 */} </Routes> </BrowserRouter> ) }
注意:全局守卫只做本地token存在性校验,接口401的跳转逻辑建议统一放到axios拦截器里处理,不用每个接口单独写。
内容的提问来源于stack exchange,提问作者Hiest
相关产品推荐
相关产品推荐

