React JS中创建受保护路由:登录后导航失效问题求助
问题解决:React受保护路由登录后无法跳转
问题根源
- 状态未同步:你的
isLoggedIn状态仅在组件挂载时通过useEffect初始化一次,登录成功后localStorage存入了token,但状态没有更新,导致路由判断仍然是false,页面停留在登录页。 - 路由判断逻辑不够健壮:直接在
Route的element中写三元表达式,状态更新不及时会导致路由无法正确渲染。
解决方案
1. 封装可复用的ProtectedRoute组件
把权限校验逻辑抽成独立组件,同时监听localStorage变化确保状态同步:
import { useState, useEffect } from 'react'; import { Navigate } from 'react-router-dom'; const ProtectedRoute = ({ children }) => { const [isLoggedIn, setIsLoggedIn] = useState(false); useEffect(() => { const checkAuthStatus = () => { const token = localStorage.getItem('token'); setIsLoggedIn(!!token); }; // 初始化时校验 checkAuthStatus(); // 监听localStorage变化(登录/登出时触发) window.addEventListener('storage', checkAuthStatus); return () => window.removeEventListener('storage', checkAuthStatus); }, []); // 可选:添加加载状态避免页面闪烁 if (isLoggedIn === null) { return <div>加载中...</div>; } return isLoggedIn ? children : <Navigate to='/login' />; };
2. 修改路由配置使用ProtectedRoute
替换原来的路由写法,让受保护路由通过封装好的组件校验:
<Route path='/login' element={<Login />} /> <Route path='/signup' element={<SignUp />} /> <Route path='/casting/calls/creation' element={ <ProtectedRoute> <CastingCallsCreation /> </ProtectedRoute> } /> <Route path='/user/film/booth' element={ <ProtectedRoute> <UserFilmPitchingBooth /> </ProtectedRoute> } />
3. 完善登录组件的跳转逻辑
登录成功后,确保更新状态并正确跳转:
import { useNavigate } from 'react-router-dom'; const Login = () => { const navigate = useNavigate(); const handleLogin = async () => { try { // 调用你的登录API const response = await fetch('/api/login', { method: 'POST', body: JSON.stringify(yourLoginData), headers: { 'Content-Type': 'application/json' } }); const data = await response.json(); if (response.ok) { // 存入token localStorage.setItem('token', data.token); // 手动触发storage事件,让ProtectedRoute更新状态 window.dispatchEvent(new Event('storage')); // 跳转目标页面 navigate('/casting/calls/creation', { replace: true }); } } catch (error) { console.error('登录失败:', error); } }; return ( // 你的登录表单结构 <div> {/* 表单输入项 */} <button onClick={handleLogin}>登录</button> </div> ); };
进阶优化:用Context管理全局登录状态
如果项目规模较大,推荐用Context统一管理登录状态,避免重复逻辑:
// AuthContext.js import { createContext, useContext, useState, useEffect } from 'react'; const AuthContext = createContext(); export const AuthProvider = ({ children }) => { const [isLoggedIn, setIsLoggedIn] = useState(false); useEffect(() => { const token = localStorage.getItem('token'); setIsLoggedIn(!!token); }, []); // 统一登录方法 const login = (token) => { localStorage.setItem('token', token); setIsLoggedIn(true); }; // 统一登出方法 const logout = () => { localStorage.removeItem('token'); setIsLoggedIn(false); }; return ( <AuthContext.Provider value={{ isLoggedIn, login, logout }}> {children} </AuthContext.Provider> ); }; export const useAuth = () => useContext(AuthContext);
然后修改ProtectedRoute和Login组件:
// 更新ProtectedRoute import { useAuth } from './AuthContext'; const ProtectedRoute = ({ children }) => { const { isLoggedIn } = useAuth(); return isLoggedIn ? children : <Navigate to='/login' />; }; // 更新Login组件 import { useNavigate } from 'react-router-dom'; import { useAuth } from './AuthContext'; const Login = () => { const navigate = useNavigate(); const { login } = useAuth(); const handleLogin = async () => { try { const response = await fetch('/api/login', { /* 请求配置 */ }); const data = await response.json(); if (response.ok) { login(data.token); navigate('/casting/calls/creation', { replace: true }); } } catch (error) { console.error('登录失败:', error); } }; // ...表单结构 };
内容的提问来源于stack exchange,提问作者hina shahzadi
相关产品推荐
相关产品推荐

