如何在用户未登录时隐藏全局侧边栏与顶部导航,仅显示登录页
问题根源
你的App组件里的token是初始化时从localStorage取的一次性值,后续即便localStorage中的token被移除,App组件不会自动重新渲染,所以Sidebar会一直显示,直到刷新页面触发组件重新初始化。而Topbar能正常隐藏,是因为它每次渲染都会重新检查token(推测你在Topbar内部做了localStorage.getItem("token")的操作),路由跳转到/login后Topbar重新渲染,自然就隐藏了。
解决办法
方法一:让App组件监听路由变化,同步token状态
用React状态存储token,每次路由切换时重新从localStorage获取最新值,触发App组件重新渲染,从而控制Sidebar的显示:
import { useState, useEffect } from 'react'; import { useLocation } from 'react-router-dom'; const App=()=> { // 初始化时读取token存入状态 const [token, setToken] = useState(localStorage.getItem("token")); const location = useLocation(); useEffect(() => { // 路由变化时重新获取token并更新状态 const currentToken = localStorage.getItem("token"); setToken(currentToken); }, [location.pathname]); return ( <div className="App"> {/* 仅当token存在时渲染Sidebar */} {token && <Sidebar/>} <Box sx={{ display: 'flex', flexDirection:'column', width: '100%', }}> <Topbar/> <main className="content"> <MainRoutes/> </main> </Box> </div> ); }
方法二:监听localStorage变化,支持跨标签页同步
如果需要跨标签页同步登录状态,可以添加storage事件监听,其他标签页操作token时,当前页面也能同步更新状态:
import { useState, useEffect } from 'react'; import { useLocation } from 'react-router-dom'; const App=()=> { const [token, setToken] = useState(localStorage.getItem("token")); const location = useLocation(); // 监听路由变化,同步token状态 useEffect(() => { setToken(localStorage.getItem("token")); }, [location.pathname]); // 监听localStorage的storage事件,跨标签页同步状态 useEffect(() => { const updateToken = (e) => { if (e.key === "token") { setToken(localStorage.getItem("token")); } }; window.addEventListener('storage', updateToken); // 组件卸载时移除监听 return () => window.removeEventListener('storage', updateToken); }, []); return ( <div className="App"> {token && <Sidebar/>} <Box sx={{ display: 'flex', flexDirection:'column', width: '100%', }}> <Topbar/> <main className="content"> <MainRoutes/> </main> </Box> </div> ); }
方法三:全局状态管理(更优雅,适合中大型项目)
用React Context封装登录状态,让所有组件共享token,登出时直接更新全局状态,无需依赖localStorage监听:
1. 创建AuthContext
// AuthContext.js import { createContext, useState, useContext, useEffect } from 'react'; const AuthContext = createContext(); export const AuthProvider = ({ children }) => { const [token, setToken] = useState(localStorage.getItem("token")); // 全局登出方法 const logout = () => { localStorage.removeItem("token"); setToken(null); }; // 全局登录方法 const login = (newToken) => { localStorage.setItem("token", newToken); setToken(newToken); }; // 监听storage变化,同步跨标签页状态 useEffect(() => { const handleStorageChange = (e) => { if (e.key === "token") { setToken(localStorage.getItem("token")); } }; window.addEventListener('storage', handleStorageChange); return () => window.removeEventListener('storage', handleStorageChange); }, []); return ( <AuthContext.Provider value={{ token, login, logout }}> {children} </AuthContext.Provider> ); }; // 自定义hooks,简化组件调用 export const useAuth = () => useContext(AuthContext);
2. 在App组件中使用
import { useAuth } from './AuthContext'; const App=()=> { const { token } = useAuth(); return ( <div className="App"> {token && <Sidebar/>} <Box sx={{ display: 'flex', flexDirection:'column', width: '100%', }}> <Topbar/> <main className="content"> <MainRoutes/> </main> </Box> </div> ); }
3. 修改Topbar的登出逻辑
import { useAuth } from './AuthContext'; import { useNavigate } from 'react-router-dom'; const Topbar = () => { const { token, logout } = useAuth(); const navigate = useNavigate(); const handleLogout = () => { logout(); navigate('/login'); }; return (token && <Box sx={{ background: `${colors.yellow[101]}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center', height: 50, }}> <Typography variant="h5">X2XBET Account Management</Typography> <Box> <IconButton onClick={handleLogout}> <LogoutRoundedIcon /> </IconButton> </Box> </Box> ) }
内容的提问来源于stack exchange,提问作者Inno Namsu
相关产品推荐
相关产品推荐

