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

React Router Dom v6刷新后navigate(-1)需点击两次的问题求助

React Router v6.3 受保护路由刷新后历史栈异常问题解决方案

核心问题分析

刷新受保护页面时,RequireAuth组件在用户状态未初始化完成前就执行了重定向到Login页,随后Login组件检测到已登录又跳转回目标页,即便使用replace: true,仍会在历史栈中残留冗余记录,导致navigate(-1)需要多次触发才能返回上一页。


方案1:在RequireAuth中增加加载状态,避免过早重定向

在权限校验组件中先判断用户状态是否处于“未初始化”状态,此时不执行重定向,而是显示加载界面,等状态确定后再处理权限逻辑。

代码实现

import { Navigate, useLocation } from 'react-router-dom';

function RequireAuth({ user, loading, children }) {
  const location = useLocation();

  // 状态未加载完成时,渲染加载组件
  if (loading) {
    return <div>加载中...</div>; // 替换为项目自定义Loading组件
  }

  // 已登录,渲染受保护页面内容
  if (user) {
    return children;
  }

  // 未登录,重定向到登录页并携带当前路径
  return <Navigate to="/login" state={{ from: location }} replace />;
}

路由配置使用

// 假设从Context/Redux获取用户状态和加载状态
const { currentUser, userLoading } = useAuth();

<Route path="/cms/*" element={
  <RequireAuth user={currentUser} loading={userLoading}>
    <CmsLayout />
  </RequireAuth>
}>
  <Route path="manage" element={<Manage />} />
  <Route path="detail/:id" element={<Detail />} />
</Route>

方案2:优化Login组件的跳转逻辑

确保登录状态的同步获取(比如从localStorage读取),并仅在必要时使用replace替换历史记录,避免冗余跳转。

代码实现

import { useLocation, useNavigate, useEffect } from 'react-router-dom';

function Login() {
  const navigate = useNavigate();
  const location = useLocation();
  const from = location.state?.from?.pathname || '/';

  // 同步获取本地存储的用户状态
  const getUser = () => JSON.parse(localStorage.getItem('user'));

  // 登录成功处理
  const handleLogin = async (formData) => {
    const user = await api.login(formData);
    localStorage.setItem('user', JSON.stringify(user));
    // 用replace替换当前Login页记录,避免历史栈残留
    navigate(from, { replace: true });
  };

  // 组件挂载时检查已登录状态,直接跳转回目标页
  useEffect(() => {
    const user = getUser();
    if (user) {
      navigate(from, { replace: true });
    }
  }, [navigate, from]);

  return (
    // 登录表单UI
    <div className="login-form">
      {/* 表单内容 */}
    </div>
  );
}

方案3:升级到React Router v6.4+,使用路由Loader做权限校验(推荐)

v6.4+新增的路由loader函数可以在路由加载前完成权限校验,从根源避免组件渲染阶段的重定向问题,是官方推荐的权限校验方式。

代码实现

import { createBrowserRouter, RouterProvider, Navigate } from 'react-router-dom';

// 全局权限校验Loader
async function authLoader() {
  // 同步获取用户状态
  const user = JSON.parse(localStorage.getItem('user'));
  if (!user) {
    // 未登录,抛出重定向响应
    throw new Response(null, {
      status: 302,
      headers: { Location: `/login?from=${window.location.pathname}` },
    });
  }
  return user;
}

// 登录页Loader,处理已登录跳转
async function loginLoader() {
  const user = JSON.parse(localStorage.getItem('user'));
  if (user) {
    const from = new URLSearchParams(window.location.search).get('from') || '/';
    throw new Response(null, {
      status: 302,
      headers: { Location: from },
    });
  }
  return null;
}

// 路由配置
const router = createBrowserRouter([
  {
    path: '/login',
    element: <Login />,
    loader: loginLoader,
  },
  {
    path: '/cms',
    element: <CmsLayout />,
    loader: authLoader, // 整个CMS路由组继承权限校验
    children: [
      { path: 'manage', element: <Manage /> },
      { path: 'detail/:id', element: <Detail /> },
    ],
  },
  // 公开路由
  { path: '/', element: <Home /> },
]);

function App() {
  return <RouterProvider router={router} />;
}

内容的提问来源于stack exchange,提问作者Hiếu Nguuyễn Minh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 16:27:40