MERN栈React应用登录后无法跳转至首页问题求助
MERN栈登录后无法跳转到首页问题排查与解决
问题现象
使用MERN栈开发Web应用,登录成功后未跳转到首页,反而重新加载登录页面。预期流程:
- 用户输入登录凭证
- 服务器验证凭证并创建Cookie
实际登录后停留在登录页,未跳转至首页。
相关代码片段
1. client/App.jsx
const App = () => { axios.defaults.baseURL = backend; axios.defaults.withCredentials = true; const [loggedIn, setLoggedIn] = useState(false); return ( <AppContext.Provider value={{setLoggedIn, loggedIn}}> <div className='App'> <Router> <Routes> <Route path='/home' Component={Home} /> <Route path='/register' Component={Register} /> <Route path='/login' Component={Login} /> </Routes> </Router> </div> </AppContext.Provider> ) }
2. client/Home.jsx
const Home = () => { const navigate = useNavigate(); const {setLoggedIn, loggedIn} = useContext(AppContext); const getAuth = async () => { try { const response = await axios.get('/'); if(response.data.status === "OK") { console.log("Already logged in"); setLoggedIn(true); } else { console.log("Not logged in"); setLoggedIn(false); } } catch(error) { console.log(error.message); } }; const logout = async () => { try { const response = await axios.get('/logout'); if(response.data.status === "OK") { setLoggedIn(false); navigate('/login'); } } catch(error) { console.log(error); } } useEffect(() => { getAuth(); console.log("After getAuth()" + loggedIn); if(!loggedIn) { navigate('/login'); } }, []); return ( <div className='bg-blue-50 h-screen flex items-center'> <div className='mx-auto'> <h1 className='block mx-auto'>Welcome to the Home Page</h1> <button className='block p-3 bg-blue-500 rounded mx-auto mt-3' onClick={logout}> Logout </button> </div> </div> ) } export default Home;
3. client/Login.jsx
const Login = () => { const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const navigate = useNavigate(); const login = async (event) => { event.preventDefault(); const response = await axios.post('/login', { username: username, password: password }); if(response.data.status === "OK") { console.log("Logged in successfully"); navigate('/home'); } else { alert("Incorrect Password"); setPassword(""); } } return ( <div className='bg-blue-50 h-screen flex items-center'> <form className='w-64 mx-auto mb-12' onSubmit={login}> <input value={username} type='text' placeholder='Username' className='block w-full p-3 mb-3 border' onChange={(event) => {setUsername(event.target.value)}}/> <input value={password} type='password' placeholder='Password' className='block w-full p-3 mb-3 border' onChange={(event) => {setPassword(event.target.value)}}/> <button className='bg-white w-full text-blue-500 block rounded-md p-3 border-3 border-blue-500'>Login</button> <Link to='/register'> <p className='text-center mt-3 text-blue-800'>New here? Register now</p> </Link> </form> </div> ) } export default Login;
4. GET /路由处理器
const verifyLogin = async (req, res) => { try { const decodedToken = await jwt.verify(req.cookies.token , process.env.JWT_SECRET); console.log(decodedToken); if(decodedToken.userID) { res.json({status: "OK", message: "Cookie is present. Allow access to protected sites"}); } else { res.json({status: "Error", message: "User login cookie not found"}); } } catch(error) { console.log("Error: " + error); res.json({status: "Error", message: "User login cookie not found"}); } }
5. POST /login路由处理器
const login = async (req,res) => { const loginInstance = await UserModel.findOne({username: req.body.username}); if(loginInstance) { if(loginInstance.password === hash(req.body.password)) { try { const token = await jwt.sign({userID: loginInstance._id}, process.env.JWT_SECRET); res.cookie("token", token).status(201).json({status: "OK", message: "Login credentials are correct. Cookie created"}) } catch(error) { console.log(error.message); } } else { res.json({status: "Error", message: "Incorrect password"}); } } else { res.json({status: "Error", message: "User not found"}); } }
6. GET /logout路由处理器
const logout = (req, res) => { res.clearCookie("token"); res.json({status: "OK", message: "Cookie has been cleared"}); }
问题根源
- Home组件useEffect时序错误:
getAuth是异步操作,但useEffect中直接在调用后判断loggedIn状态,此时setLoggedIn还未完成状态更新,导致错误执行navigate('/login'),把用户从首页强制跳回登录页。 - 状态更新异步特性忽略:
setLoggedIn是异步的,console.log("After getAuth()" + loggedIn)打印的是旧状态,无法反映最新登录状态。
修复方案
1. 调整Home组件的状态监听逻辑
修改Home组件的useEffect,拆分异步验证和状态监听逻辑,确保状态更新后再判断跳转:
const Home = () => { const navigate = useNavigate(); const {setLoggedIn, loggedIn} = useContext(AppContext); const getAuth = async () => { try { const response = await axios.get('/'); if(response.data.status === "OK") { console.log("Already logged in"); setLoggedIn(true); } else { console.log("Not logged in"); setLoggedIn(false); navigate('/login'); } } catch(error) { console.log(error.message); setLoggedIn(false); navigate('/login'); } }; const logout = async () => { try { const response = await axios.get('/logout'); if(response.data.status === "OK") { setLoggedIn(false); navigate('/login'); } } catch(error) { console.log(error); } } useEffect(() => { getAuth(); }, []); // 单独监听loggedIn状态变化,未登录时跳转 useEffect(() => { if(!loggedIn) { navigate('/login'); } }, [loggedIn, navigate]); return ( <div className='bg-blue-50 h-screen flex items-center'> <div className='mx-auto'> <h1 className='block mx-auto'>Welcome to the Home Page</h1> <button className='block p-3 bg-blue-500 rounded mx-auto mt-3' onClick={logout}> Logout </button> </div> </div> ) } export default Home;
2. 登录成功后同步更新全局状态
在Login组件中登录成功后,立即更新全局loggedIn状态,确保Home组件能获取到正确的登录状态:
const Login = () => { const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const navigate = useNavigate(); const {setLoggedIn} = useContext(AppContext); const login = async (event) => { event.preventDefault(); const response = await axios.post('/login', { username: username, password: password }); if(response.data.status === "OK") { console.log("Logged in successfully"); setLoggedIn(true); // 同步更新全局登录状态 navigate('/home'); } else { alert("Incorrect Password"); setPassword(""); } } return ( <div className='bg-blue-50 h-screen flex items-center'> <form className='w-64 mx-auto mb-12' onSubmit={login}> <input value={username} type='text' placeholder='Username' className='block w-full p-3 mb-3 border' onChange={(event) => {setUsername(event.target.value)}}/> <input value={password} type='password' placeholder='Password' className='block w-full p-3 mb-3 border' onChange={(event) => {setPassword(event.target.value)}}/> <button className='bg-white w-full text-blue-500 block rounded-md p-3 border-3 border-blue-500'>Login</button> <Link to='/register'> <p className='text-center mt-3 text-blue-800'>New here? Register now</p> </Link> </form> </div> ) } export default Login;
3. 优化服务器Cookie配置(可选)
为Cookie添加安全配置,避免生产环境下Cookie无法正常携带:
const login = async (req,res) => { const loginInstance = await UserModel.findOne({username: req.body.username}); if(loginInstance) { if(loginInstance.password === hash(req.body.password)) { try { const token = await jwt.sign({userID: loginInstance._id}, process.env.JWT_SECRET); res.cookie("token", token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict', maxAge: 24 * 60 * 60 * 1000 }).status(201).json({status: "OK", message: "Login credentials are correct. Cookie created"}) } catch(error) { console.log(error.message); res.status(500).json({status: "Error", message: "Failed to create token"}); } } else { res.json({status: "Error", message: "Incorrect password"}); } } else { res.json({status: "Error", message: "User not found"}); } }
验证步骤
- 登录时查看浏览器控制台,确认
Logged in successfully输出,且loggedIn状态更新为true - 登录成功后检查浏览器Cookie,确认
token已存在 - 访问首页时,确认
Already logged in输出,页面正常加载且无跳转回登录页的情况
内容的提问来源于stack exchange,提问作者Vedant Bhandare
相关产品推荐
相关产品推荐

