React私有路由无法识别已登录状态问题求助
已登录用户访问/login路径时,AuthContext的isAuthenticated始终为false的问题
我使用React Context存储已登录用户的信息:
import React, { createContext, Dispatch, SetStateAction, useEffect, useState } from "react"; interface User { name?: string; email?: string; rating?: number; } interface LoggedUserInterface { isAuthenticated: boolean; user: User; } interface IAuthContext extends LoggedUserInterface { setLoggedUser: Dispatch<SetStateAction<IAuthContext>>; } type Props = { children?: React.ReactNode; }; // Default value for IAuthContext const defaultAuthContextValue: IAuthContext = { isAuthenticated: false, user: { name: undefined, email: undefined, rating: undefined, }, setLoggedUser: () => {}, // You might want to provide a default implementation }; const AuthContext = createContext<IAuthContext>(defaultAuthContextValue); const AuthProvider: React.FC<Props> = ({ children }) => { const [loggedUser, setLoggedUser] = useState<IAuthContext>(defaultAuthContextValue); useEffect(() => { async function getWhoAmI() { try { const response = await fetch("http://localhost:8080/api/v1/profile/status", { method: "GET", credentials: "include", }); if (response.ok) { const data = await response.json(); setLoggedUser((prev) => ({ ...prev, isAuthenticated: data.isAuthenticated, user: data })); } else { setLoggedUser((prev) => ({ ...prev, isAuthenticated: false, user: {} })); console.error("Error checking authentication status:", response.statusText); } } catch (error: any) { setLoggedUser((prev) => ({ ...prev, isAuthenticated: false, user: {} })); console.error("Error checking authentication status:", error.message); } } getWhoAmI(); }, []); return ( <AuthContext.Provider value={{ ...loggedUser, setLoggedUser }}> {children} </AuthContext.Provider> ); }; export { AuthContext, AuthProvider };
我还有一个登录页面,希望阻止已登录用户访问/login路径:
import React, {useContext, useEffect, useState} from 'react'; import {Button, Paper, TextField, Typography} from '@mui/material'; import {useNavigate} from 'react-router-dom'; import './Login.css'; import {AuthContext} from "../context/AuthContext"; const Login: React.FC = () => { const [formData, setFormData] = useState({ email: '', password: '', }); const navigate = useNavigate(); const authContext = useContext(AuthContext); const {setLoggedUser} = authContext; const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => { const {name, value} = event.target; setFormData((prevFormData) => ({ ...prevFormData, [name]: value, })); }; const loginUser = async () => { const endpoint = 'http://localhost:8080/api/v1/auth/login'; try { const response = await fetch(endpoint, { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(formData), }); if (response.ok) { const data = await response.json(); setLoggedUser({ ...authContext, isAuthenticated: true, user: { name: data.name, email: data.email, rating: data.rating, }, }); // Redirect to the desired page upon successful login navigate('/'); } else { const errorData = await response.json(); console.error('Login failed:', errorData.message); } } catch (error: any) { console.error('Error during login:', error.message); console.log(error); } }; const handleSubmit = () => { loginUser(); }; return ( <> <div id="login-wrapper"> <Paper elevation={3} id="login-container"> <Typography variant="h2" align="center" gutterBottom> Login </Typography> <form style={{display: 'flex', flexDirection: 'column', gap: '16px', width: '100%'}}> <TextField label="Email" type="email" variant="outlined" onChange={handleInputChange} name="email"/> <TextField label="Password" type="password" variant="outlined" onChange={handleInputChange} name="password"/> <Button variant="contained" color="primary" onClick={handleSubmit}> Login </Button> </form> </Paper> </div> </> ); }; export default Login;
为此,我使用了私有路由:
import React, { ReactNode } from "react"; import { Navigate, Outlet } from "react-router-dom"; interface ProtectedRouteProps { isAllowed: boolean; redirectTo?: string; children?: ReactNode; } export default function ProtectedRoute({ isAllowed, redirectTo = "/", children }: ProtectedRouteProps) { console.log("LOGGED IS " + isAllowed) if (isAllowed) { return <Navigate to={redirectTo} />; } return children ? <> {children} </> : <Outlet />; }
使用方式如下:
function App() { const authContext = useContext(AuthContext); return ( <div className="container"> <AuthProvider> <Header/> <Routes> <Route path="/" element={<Home/>}/> <Route path="/register" element={<Register/>}/> <Route path="/login" element={ <ProtectedRoute isAllowed={authContext.isAuthenticated} redirectTo='/'> <Login/> </ProtectedRoute>} /> <Route path="/howto" element={<HowTo/>}/> </Routes> <Footer/> </AuthProvider> </div> ); }
当我在登录页面登录后,会跳转到"/"页面,但当我尝试通过URL访问/login时,并未如预期跳转回"/"(此时用户已登录)。我检查条件发现,当通过URL访问/login时,authContext.isAuthenticated始终为false。
这是为什么?我的Header组件可以根据用户是否登录正确显示内容,说明Context状态并未改变。那为什么这里会是false?
感谢您的帮助!
内容的提问来源于stack exchange,提问作者Johnyb
相关产品推荐
相关产品推荐

