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
相关产品推荐
相关产品推荐

