React中LocalStorage的EventListener失效问题求助
问题原因
window的storage事件仅在同源的其他窗口/标签页修改localStorage时才会触发,同一页面内修改localStorage不会触发该事件。你当前是在登录页面(同一页面)存入token,所以监听的storage事件不会执行,导致App组件的token状态没有更新,自然不会自动切换到Home组件。
解决方案
方案1:登录成功后手动更新token状态
直接在登录成功的回调中更新App组件的token状态,不需要依赖storage事件。
修改App.js
将setToken作为props传递给Login组件:
import { useEffect, useState } from "react"; import "./App.css"; import Home from "./Component/Home"; import Login from "./Component/Login"; import { Routes, Route, BrowserRouter } from "react-router-dom"; function App() { const [token, setToken] = useState(!!localStorage.getItem('token')); // 保留跨标签页的storage监听(可选,用于其他标签页修改token时同步状态) useEffect(() => { const handleStorageChange = () => { setToken(!!localStorage.getItem('token')); }; window.addEventListener('storage', handleStorageChange); return () => window.removeEventListener('storage', handleStorageChange); }, []); return ( <BrowserRouter> <Routes> {token ? ( <Route path="/" element={<Home />} /> ) : ( <Route path="/" element={<Login setToken={setToken} />} /> )} </Routes> </BrowserRouter> ); } export default App;
修改Login组件的登录逻辑
在登录成功后调用setToken更新状态:
const handleSubmit = (e) => { e.preventDefault(); axios .post("AUTH_API", { username: username, password: pass, }) .then((res) => { console.log(res.data); localStorage.setItem("token", res.data.token); setToken(true); // 手动更新App组件的token状态 }) .catch(function (error) { console.log(error); toast("Please Enter Valid Details!"); }); };
方案2:使用React Context管理全局认证状态(推荐)
用Context统一管理token状态,同时处理当前页面和跨标签页的token变化,代码更易维护。
创建AuthContext.js
import { createContext, useContext, useState, useEffect } from 'react'; const AuthContext = createContext(); export function AuthProvider({ children }) { const [isAuthenticated, setIsAuthenticated] = useState(!!localStorage.getItem('token')); // 登录:存入token并更新状态 const login = (token) => { localStorage.setItem('token', token); setIsAuthenticated(true); }; // 登出:移除token并更新状态 const logout = () => { localStorage.removeItem('token'); setIsAuthenticated(false); }; // 监听跨标签页的storage变化 useEffect(() => { const handleStorageChange = () => { setIsAuthenticated(!!localStorage.getItem('token')); }; window.addEventListener('storage', handleStorageChange); return () => window.removeEventListener('storage', handleStorageChange); }, []); return ( <AuthContext.Provider value={{ isAuthenticated, login, logout }}> {children} </AuthContext.Provider> ); } export function useAuth() { return useContext(AuthContext); }
修改App.js
用AuthProvider包裹路由,并根据全局状态渲染组件:
import { Routes, Route, BrowserRouter } from "react-router-dom"; import { AuthProvider, useAuth } from "./AuthContext"; import Home from "./Component/Home"; import Login from "./Component/Login"; function AppContent() { const { isAuthenticated } = useAuth(); return ( <Routes> {isAuthenticated ? ( <Route path="/" element={<Home />} /> ) : ( <Route path="/" element={<Login />} /> )} </Routes> ); } function App() { return ( <AuthProvider> <BrowserRouter> <AppContent /> </BrowserRouter> </AuthProvider> ); } export default App;
修改Login组件
使用useAuth获取login方法,登录时调用:
import { useAuth } from "../AuthContext"; // ... const { login } = useAuth(); const handleSubmit = (e) => { e.preventDefault(); axios .post("AUTH_API", { username: username, password: pass, }) .then((res) => { console.log(res.data); login(res.data.token); // 调用Context中的登录方法 }) .catch(function (error) { console.log(error); toast("Please Enter Valid Details!"); }); };
方案3:优化路由跳转逻辑(可选)
更符合路由规范的写法是使用Navigate组件进行页面跳转,而不是在同一路径渲染不同组件:
import { Routes, Route, BrowserRouter, Navigate } from "react-router-dom"; // ...(结合方案1或方案2的状态管理) function AppContent() { const { isAuthenticated } = useAuth(); return ( <Routes> <Route path="/login" element={<Login />} /> <Route path="/home" element={<Home />} /> <Route path="/" element={isAuthenticated ? <Navigate to="/home" /> : <Navigate to="/login" />} /> </Routes> ); }
内容的提问来源于stack exchange,提问作者sahil butani
相关产品推荐
相关产品推荐

