如何基于localStorage判断登录状态实现React Router路由重定向?
原代码问题点
- 用普通变量
userLogged存储登录状态,useEffect内修改变量不会触发组件重渲染,路由无法响应状态变化 - 依赖
storage事件同步状态逻辑错误:该事件仅在非当前标签页修改localStorage时触发,当前页面执行setItem不会触发该事件,同页面登录/登出时状态完全不会更新 - 条件渲染路由逻辑错误:未登录状态下根本没有注册
/dashboard路由,手动输入地址会直接匹配失败,无法实现自动跳转 - 未做类型转换:localStorage仅支持存储字符串,你存入的布尔值
true/false会被转成"true"/"false",直接判断会出现"false"被识别为真值的bug React.StrictMode位置错误,该组件应该放在入口文件包裹根组件,不能写在App的渲染逻辑内
修正方案
核心思路是放弃条件渲染路由,通过路由守卫做访问控制,用状态管理登录态触发重渲染,同页面登录/登出操作完成后主动执行路由跳转,storage事件仅用来做多标签页状态同步。
第一步:修改App.js代码
import { useEffect, useState } from 'react'; import { BrowserRouter as Router, Routes, Route, Navigate, useLocation } from 'react-router-dom'; import Home from './pages/Home'; import Dashboard from './pages/Dashboard'; // 受保护路由守卫:未登录访问时重定向到/home function RequireAuth({ children }) { const location = useLocation(); const isLogged = localStorage.getItem("userLogged") === "true"; if (!isLogged) { return <Navigate to="/home" replace state={{ from: location }} />; } return children; } // 公开路由守卫:已登录访问时重定向到/dashboard function RedirectIfLogged({ children }) { const isLogged = localStorage.getItem("userLogged") === "true"; if (isLogged) { return <Navigate to="/dashboard" replace />; } return children; } function App() { // 初始化时直接读取localStorage值,统一转成布尔值 const [userLogged, setUserLogged] = useState(() => { return localStorage.getItem("userLogged") === "true"; }); useEffect(() => { const syncLoginStatus = () => { setUserLogged(localStorage.getItem("userLogged") === "true"); }; // 仅用来同步多标签页的登录状态变化 window.addEventListener("storage", syncLoginStatus); return () => window.removeEventListener("storage", syncLoginStatus); }, []); return ( <div className="App"> <Router> <Routes> {/* 根路径自动适配登录状态跳转 */} <Route path="/" element={userLogged ? <Navigate to="/dashboard" replace /> : <Navigate to="/home" replace />} /> {/* home页:已登录不可访问 */} <Route path="/home" element={ <RedirectIfLogged> <Home /> </RedirectIfLogged> } /> {/* dashboard页:未登录不可访问 */} <Route path="/dashboard" element={ <RequireAuth> <Dashboard /> </RequireAuth> } /> {/* 404兜底 */} <Route path="*" element={userLogged ? <Navigate to="/dashboard" replace /> : <Navigate to="/home" replace />} /> </Routes> </Router> </div> ); } export default App;
第二步:调整入口文件index.js
把React.StrictMode移到入口文件,不要写在App内部:
import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import './index.css'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <App /> </React.StrictMode> );
第三步:补全页面内的登录/登出跳转逻辑
因为同页面修改localStorage不会触发storage事件,登录/登出操作完成后要主动执行跳转:
- Home页面登录逻辑示例:
import { useNavigate } from 'react-router-dom'; function Home() { const navigate = useNavigate(); const handleLogin = () => { // 省略你的登录校验逻辑,登录成功后执行 localStorage.setItem("userLogged", "true"); // 注意存字符串格式的"true" navigate("/dashboard", { replace: true }); }; return ( // 你的页面代码 <button onClick={handleLogin}>登录</button> ) } export default Home;
- Dashboard页面登出逻辑示例:
import { useNavigate } from 'react-router-dom'; function Dashboard() { const navigate = useNavigate(); const handleLogout = () => { localStorage.setItem("userLogged", "false"); // 也可以直接用localStorage.removeItem("userLogged")清除标识 navigate("/home", { replace: true }); }; return ( // 你的页面代码 <button onClick={handleLogout}>退出登录</button> ) } export default Dashboard;
实现效果
- 登录成功后自动跳转到
/dashboard,手动输入/home地址也会被自动拦回dashboard - 登出后自动跳回
/home,手动输入/dashboard地址会被自动拦回home - 多标签页下登录/登出状态会自动同步,A标签页登出后B标签页会自动跳转对应页面
内容的提问来源于stack exchange,提问作者user18150656
相关产品推荐
相关产品推荐

