登录后React NavBar未自动刷新:路由跳转不更新仅刷新页面生效
问题描述
登录成功后通过navigate跳转到/home页面,NavBar组件并未自动更新,仍显示未登录状态的导航链接,仅手动刷新页面后,才会渲染登录后的导航内容。
相关代码
App组件
function App() { const [token, setToken] = useState(); useEffect(() => { const auth = localStorage.getItem('auth_token'); setToken(auth); }, [token]); return ( <div> <FullNavBar /> <Routes> <Route path='/login' element={<Login />}></Route> <Route path='/register' element={<Register />}></Route> <Route path='/forgot_password' element={<ForgotPassword />}></Route> <Route element={<ProtectedRoutes />}> <Route path='/home' element={<Home />}></Route> <Route path='/active_lottery' element={<Activelottery />}></Route> </Route> </Routes> </div> ); } export default App;
受保护路由组件
import { Navigate, Outlet } from 'react-router'; const authorization = () => { const token = localStorage.getItem('auth_token'); return token ? true : false; }; const ProtectedRoutes = () => { const isAuth = authorization(); return isAuth ? <Outlet /> : <Navigate to='/login' />; }; export default ProtectedRoutes;
登录跳转逻辑
const onSubmitHandler = async (event) => { event.preventDefault(); await axios .post( 'url/dev/user/login', loginForm, ) .then((response) => { localStorage.setItem( 'auth_token', response.data.AuthenticationResult.AccessToken, ); toast.success('Your in now'); setTimeout(() => { navigate('/home'); }, 1000); }) .catch((err) => { console.log(err); toast.error(err.response.data.message); }); };
FullNavBar组件
export default function FullNavBar() { const [menuOpen, setMenuOpen] = useState(false); const [auth, setAuth] = useState(false); const token = localStorage.getItem('auth_token'); useEffect(() => { if (token) { console.log(token); setAuth(true); } }, [auth]); return ( <div className="bg-gradient-to-r from-yellow-400 to-yellow-500"> <Navbar token={auth} menuOpen={menuOpen} setMenuOpen={setMenuOpen} /> {menuOpen && <MobileMenu>{navLinks(auth)}</MobileMenu>} </div> ); } const navLinks = token => { let pages = ['About', 'Contact', 'Login']; if (token) { pages = ['Buy', 'Tickets', 'History', 'Profile']; } return pages.map(page => page === 'Profile' ? ( <button key={page} className="inline-block"> {' '} <img className="rounded-full w-11 h-11" src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQhumf_G7azRo-qCcnB533PPwZx386EK1cozzMAMEtW3A&s" ></img> </button> ) : page === 'Tickets' ? ( <a key={page} className="no-underline text-gray-800 font-semibold hover:text-gray-500 relative bottom-4" href={`${page.toLowerCase()}`} > 100 {page} </a> ) : ( <a key={page} className="no-underline text-gray-800 font-semibold hover:text-gray-500 relative bottom-4" href={`${page.toLowerCase()}`} > {page} </a> ), ); };
问题原因
- App组件状态未同步:App中的
token状态仅在初始化时从localStorage读取一次,登录后localStorage更新但App的token状态未更新,导致FullNavBar没有收到新props触发重渲染。 - FullNavBar监听逻辑错误:
FullNavBar中的useEffect依赖是auth自身,而非token,且token仅在组件初始化时读取,无法响应localStorage的后续变化。
解决方案
方案一:Props传递+Storage监听(适合小型项目)
1. 修改App组件
让App的token状态响应localStorage变化,并传递给FullNavBar:
function App() { const [token, setToken] = useState(localStorage.getItem('auth_token')); // 监听localStorage中auth_token的变化 useEffect(() => { const handleStorageUpdate = (e) => { if (e.key === 'auth_token') { setToken(e.newValue); } }; window.addEventListener('storage', handleStorageUpdate); return () => window.removeEventListener('storage', handleStorageUpdate); }, []); return ( <div> <FullNavBar token={token} /> <Routes> {/* 路由配置不变 */} </Routes> </div> ); } export default App;
2. 修改FullNavBar组件
依赖传入的token更新auth状态:
export default function FullNavBar({ token }) { const [menuOpen, setMenuOpen] = useState(false); const [auth, setAuth] = useState(!!token); useEffect(() => { // 当token变化时更新auth状态 setAuth(!!token); }, [token]); return ( <div className="bg-gradient-to-r from-yellow-400 to-yellow-500"> <Navbar token={auth} menuOpen={menuOpen} setMenuOpen={setMenuOpen} /> {menuOpen && <MobileMenu>{navLinks(auth)}</MobileMenu>} </div> ); } // navLinks函数保持不变
方案二:React Context管理全局状态(更优雅,适合中大型项目)
1. 创建AuthContext
// AuthContext.js import { createContext, useContext, useState } from 'react'; const AuthContext = createContext(); export function AuthProvider({ children }) { const [token, setToken] = useState(localStorage.getItem('auth_token')); // 登录时更新token和localStorage const login = (newToken) => { localStorage.setItem('auth_token', newToken); setToken(newToken); }; // 登出时清除token const logout = () => { localStorage.removeItem('auth_token'); setToken(null); }; return ( <AuthContext.Provider value={{ token, login, logout }}> {children} </AuthContext.Provider> ); } // 自定义hooks方便组件使用 export function useAuth() { return useContext(AuthContext); }
2. 修改App组件,用AuthProvider包裹
import { AuthProvider } from './AuthContext'; function App() { return ( <AuthProvider> <div> <FullNavBar /> <Routes> {/* 路由配置不变 */} </Routes> </div> </AuthProvider> ); } export default App;
3. 修改Login组件的登录逻辑
调用Context中的login方法更新全局状态:
import { useAuth } from './AuthContext'; import { useNavigate } from 'react-router-dom'; const Login = () => { const { login } = useAuth(); const navigate = useNavigate(); const onSubmitHandler = async (event) => { event.preventDefault(); await axios .post('url/dev/user/login', loginForm) .then((response) => { const accessToken = response.data.AuthenticationResult.AccessToken; login(accessToken); // 更新全局登录状态 toast.success('Your in now'); setTimeout(() => { navigate('/home'); }, 1000); }) .catch((err) => { console.log(err); toast.error(err.response.data.message); }); }; // 其他组件逻辑 };
4. 修改FullNavBar组件,使用useAuth获取状态
import { useAuth } from './AuthContext'; export default function FullNavBar() { const { token } = useAuth(); const [menuOpen, setMenuOpen] = useState(false); const auth = !!token; // 直接根据token判断登录状态 return ( <div className="bg-gradient-to-r from-yellow-400 to-yellow-500"> <Navbar token={auth} menuOpen={menuOpen} setMenuOpen={setMenuOpen} /> {menuOpen && <MobileMenu>{navLinks(auth)}</MobileMenu>} </div> ); } // navLinks函数保持不变
内容的提问来源于stack exchange,提问作者Hulubina
相关产品推荐
相关产品推荐

