You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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"});
}

问题根源

  1. Home组件useEffect时序错误:getAuth是异步操作,但useEffect中直接在调用后判断loggedIn状态,此时setLoggedIn还未完成状态更新,导致错误执行navigate('/login'),把用户从首页强制跳回登录页。
  2. 状态更新异步特性忽略: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"});
    }
}

验证步骤

  1. 登录时查看浏览器控制台,确认Logged in successfully输出,且loggedIn状态更新为true
  2. 登录成功后检查浏览器Cookie,确认token已存在
  3. 访问首页时,确认Already logged in输出,页面正常加载且无跳转回登录页的情况

内容的提问来源于stack exchange,提问作者Vedant Bhandare

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 16:07:01