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

react-router-dom v6中如何获取URL对应的上一个路由

React Router v6 环境下获取上一个路由、实现受保护路由重定向的落地方案

这个需求完全可实现,你之前遇到的无限循环、旧API失效问题,核心是实现思路和作用域选择错误,按以下步骤实现即可覆盖所有场景:

问题根因说明

  • 你写的useEffect触发无限循环的核心原因:上一个路径的状态存在受保护路由组件内部,组件挂载时的状态更新、鉴权重定向逻辑、location监听三者触发时机冲突,会反复触发重渲染。
  • 网上旧方案适配性差的原因:useHistory是React Router v5及更早版本的API,v6版本已替换为useNavigate;基于Link组件传state的方案仅能覆盖点击跳转场景,无法覆盖用户手动输入地址、书签跳转、浏览器前进后退的场景。
    核心避坑点:上一个路由的记录逻辑不能放在单个页面组件内,必须提升到路由根层级统一维护

具体实现代码

1. 全局维护路由上下文

在路由根层级用useRef存储上一个路径(ref值更新不会触发重渲染,从根源避免循环问题),不要用组件state存路径记录:

import { createContext, useContext, useEffect, useRef } from 'react';
import { useLocation, Navigate } from 'react-router-dom';

const RouteContext = createContext(null);

// 上下文组件需包裹在<BrowserRouter/>内部的最外层
export function RouteProvider({ children }) {
  const location = useLocation();
  // 初始化默认兜底路径,可根据业务调整为登录页/首页
  const prevPathRef = useRef('/home');
  const currentPathRef = useRef(location.pathname);
  // 替换为项目内所有不需要鉴权的公开路由路径
  const publicPaths = ['/home', '/contact', '/login'];

  useEffect(() => {
    // 仅当访问公开路由时才更新路径记录,避免受保护路径被计入历史
    if (publicPaths.includes(location.pathname)) {
      prevPathRef.current = currentPathRef.current;
      currentPathRef.current = location.pathname;
    }
  }, [location.pathname]);

  const getPrevPath = () => prevPathRef.current;

  return (
    <RouteContext.Provider value={{ getPrevPath }}>
      {children}
    </RouteContext.Provider>
  );
}

// 自定义hook供业务组件调用
export function usePrevRoute() {
  const context = useContext(RouteContext);
  if (!context) throw new Error('usePrevRoute必须在RouteProvider内部使用');
  return context;
}

2. 封装通用受保护路由组件

鉴权失败时直接读取上一个路径做重定向,注意加replace参数避免污染浏览器历史栈:

export function ProtectedRoute({ children }) {
  const { getPrevPath } = usePrevRoute();
  // 替换为项目实际的鉴权判断逻辑,比如读取全局状态/localStorage中的token
  const isAuthenticated = Boolean(localStorage.getItem('access_token'));

  if (!isAuthenticated) {
    const redirectTarget = getPrevPath() || '/home';
    return <Navigate to={redirectTarget} replace />;
  }
  return children;
}

3. 路由配置示例

按层级包裹组件即可生效:

import { BrowserRouter, Routes, Route } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <RouteProvider>
        <Routes>
          <Route path="/home" element={<Home />} />
          <Route path="/contact" element={<Contact />} />
          <Route path="/login" element={<Login />} />
          {/* 受保护路由用ProtectedRoute包裹 */}
          <Route path="/profile" element={
            <ProtectedRoute>
              <Profile />
            </ProtectedRoute>
          } />
        </Routes>
      </RouteProvider>
    </BrowserRouter>
  );
}

补充说明

  • 该实现可以覆盖所有跳转场景:包括手动输入地址、浏览器前进后退、书签访问、代码触发跳转、Link组件跳转。
  • 针对用户首次打开网站就直接访问受保护路由的极端场景,会自动跳转到你设置的默认兜底路径,不会出现空白或报错。
  • 业务组件内如果需要获取上一个路由,直接调用const { getPrevPath } = usePrevRoute()即可拿到对应路径值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 14:09:18