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

useNavigate()无法跳转至/protected页面问题求助

登录成功后React Router navigate无法跳转问题解决

问题描述

API运行正常,能返回token并保存至localStorage,但调用navigate('/protected')后页面无法跳转;刷新页面可直接进入/protected页面,不想使用强制刷新的临时方案。

相关代码

LoginForm.js

import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useMutation } from 'react-query';
import api from './api';

function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');

  const navigate = useNavigate();

  const handleLogin = async () => {
    const response = await api.post('/authenticate', { email, password });
    return response.data;
  };

  const { mutate, isLoading } = useMutation(handleLogin, {
    onSuccess: (data) => {
      localStorage.setItem('token', data.token);
      console.log(data.token);
      navigate('/protected');
    },
    onError: (error) => {
      setError(error.response.data);
    },
  });

  const handleSubmit = (e) => {
    mutate();
    e.preventDefault();
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Email:
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
      </label>
      <label>
        Password:
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
        />
      </label>
      {error && <div>{error}</div>}
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Logging in...' : 'Login'}
      </button>
    </form>
  );
}

export default LoginForm;

App.js

import {
  BrowserRouter as Router,
  Routes,
  Route,
  Navigate
} from 'react-router-dom';
import { useQuery } from 'react-query';
import LoginForm from './LoginForm';
import ProtectedResource from './ProtectedResource';
import api from './api';

function App() {
  const { isLoading, error, data: isLoggedIn } = useQuery('isLoggedIn', async () => {
    const token = localStorage.getItem('token');
    if (token) {
      try {
        await api.get('/resource', {
          headers: {
            Authorization: `Bearer ${token}`,
          },
        });
        return true;
      } catch (error) {
        if (error.response.status === 401) {
          localStorage.removeItem('token');
        }
        return false;
      }
    } else {
      return false;
    }
  });

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>An error occurred: {error.message}</p>;

  return (
    <Router>
      <Routes>
        <Route
          path="/"
          element={isLoggedIn ? <Navigate to="/protected" /> : <LoginForm />}
        />
        <Route
          path="/protected"
          element={isLoggedIn ? <ProtectedResource /> : <Navigate to="/" />}
        />
      </Routes>
    </Router>
  );
}

export default App;

问题原因

App组件中通过useQuery('isLoggedIn')判断用户是否登录,这个查询默认只会在组件挂载时执行一次。登录成功后虽然localStorage里的token已更新,但useQuery不会自动重新执行,导致isLoggedIn仍为false。此时调用navigate('/protected')后,路由守卫会因为isLoggedIn为false自动跳回首页,所以看起来页面没有跳转。

解决方案

方法一:登录成功后手动触发查询刷新

利用React Query的useQueryClient,在登录成功后强制刷新isLoggedIn查询,让App组件获取最新的登录状态:

修改LoginForm.js:

  1. 导入useQueryClient:
import { useMutation, useQueryClient } from 'react-query';
  1. 在组件内初始化queryClient:
const queryClient = useQueryClient();
  1. 在onSuccess回调中添加刷新逻辑:
onSuccess: (data) => {
  localStorage.setItem('token', data.token);
  console.log(data.token);
  // 刷新isLoggedIn查询,更新登录状态
  queryClient.invalidateQueries('isLoggedIn');
  navigate('/protected');
},

方法二:优化表单提交逻辑(非核心,但建议调整)

原代码中handleSubmit先调用mutate()再执行e.preventDefault(),顺序有误,建议调整为:

const handleSubmit = (e) => {
  e.preventDefault();
  mutate();
};

这样修改后,登录成功后isLoggedIn会被更新为true,navigate('/protected')就能正常跳转到保护页面,无需刷新。

内容的提问来源于stack exchange,提问作者Alex Labra

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 03:53:12