react-router-navigation-summary
Table of Contents
核心导航方式
React Router 提供了两种主要的导航方式:声明式 (Declarative) 和 编程式 (Imperative)。
1. 声明式导航 (<Link> / <NavLink>)
最常用的方式,用于创建应用内的链接。
<Link>: 基础链接组件,相当于 HTML 的<a>标签,但不会触发页面完全刷新。<NavLink>: 特殊的<Link>,知道自己是否处于“激活”状态。
import { Link, NavLink } from "react-router";
// 基础用法<Link to="/home">Go Home</Link>
// NavLink 激活状态样式<NavLink to="/messages" className={({ isActive, isPending }) => isPending ? "pending" : isActive ? "active" : "" }> Messages</NavLink>2. 编程式导航 (useNavigate)
当需要在执行某些逻辑(如提交表单、登录成功)后跳转时使用。
import { useNavigate } from "react-router";
function LoginForm() { const navigate = useNavigate();
async function handleSubmit(event) { event.preventDefault(); await login(); // 跳转到主页,replace: true 表示替换当前历史记录,用户无法回退到登录页 navigate("/dashboard", { replace: true }); // 或者后退一步 // navigate(-1); }
return <form onSubmit={handleSubmit}>...</form>;}3. 组件式重定向 (<Navigate>)
用于在渲染时立即重定向。常见于保护路由(Protected Routes)。
import { Navigate } from "react-router";
const ProtectedRoute = ({ user, children }) => { if (!user) { // 没登录直接跳去登录页,replace 同样是为了防止回退死循环 return <Navigate to="/login" replace />; } return children;};4. Loader/Action 中的重定向 (redirect)
在 v6.4+ (Data Router) 中,推荐在 loader 或 action 中处理重定向,而不是在组件渲染时。
import { redirect } from "react-router";
export const loader = async () => { const user = await getUser(); if (!user) { throw redirect("/login"); } return { user };};避坑指南 (Common Pitfalls)
1. 相对路径 vs 绝对路径
- 绝对路径: 以
/开头(如/about),总是从根路径开始匹配。 - 相对路径: 不以
/开头(如details),基于当前路由路径进行拼接。
坑点: 如果你在 /users/123 页面,点击 <Link to="edit">,会跳转到 /users/123/edit。但如果你写成了 <Link to="/edit">,则会跳到根目录下的 /edit。
另外,.. 在相对路径中的行为是指向路由层级的父级,而不是 URL 路径的父级(虽然通常一致,但在嵌套路由中可能有区别)。
2. replace: true 的重要性
场景: 登录页 -> 仪表盘。
如果不加 replace: true,用户点击浏览器“后退”按钮会回到登录页(此时用户已登录,可能又被重定向回仪表盘,体验很差)。
解决: 状态改变导致的跳转(Login -> Home, Form Success -> List),通常都应该使用 { replace: true }。
3. useEffect 中使用 useNavigate 的竞态/双重调用
坑点: 在 useEffect 中调用 navigate 可能会在 React Strict Mode (开发环境) 下执行两次,或者与其他状态更新产生冲突。
建议:
- 优先使用
loader中的redirect处理前置跳转逻辑。 - 如果是交互后的跳转,放在事件处理函数中,而不是
useEffect。 - 如果必须在
useEffect中跳转,确保依赖项正确,并且该逻辑是幂等的。
4. 路由状态 (state) 的易失性
你可以通过 state 传递数据:
navigate("/target", { state: { from: "home", id: 123 } });在目标页面获取:
const location = useLocation();const { from, id } = location.state || {}; // 必须处理 null/undefined坑点: 用户刷新页面或者直接复制链接在新标签页打开时,state 会丢失(变为 null)。
解决: state 仅用于非关键的 UI 增强(如“返回上一页”的路径记录、Toast 消息)。核心业务数据(如 ID)必须放在 URL 参数 (params 或 searchParams) 中,以便刷新后能重新获取。
5. 阻塞导航 (useBlocker)
在旧版本中常用的 <Prompt> 已被移除。v6 使用 useBlocker(需要 Data Router 支持)。
坑点: 浏览器对于 beforeunload 事件的限制越来越严,自定义 UI 的阻塞导航只能在应用内路由跳转时生效,无法完全阻止用户关闭浏览器标签页或刷新(只能弹默认的浏览器原生对话框)。
6. <a href="..."> vs <Link to="...">
新手误区: 使用 <a> 标签跳转内部页面。
后果: 浏览器会发起全新的页面请求,React 应用会被卸载并重新加载,导致:
- 状态丢失(Redux/Context 重置)。
- 性能下降(需要重新下载/解析 JS)。
原则: 内部跳转永远使用
<Link>,外部链接才使用<a>。
总结
- 优先声明式: 能用
<Link>解决的,不要写onClick={() => navigate(...)}。 - 拥抱 Data Router: v6.4+ 的
loader/action模式让重定向逻辑更清晰,避免了组件渲染后的“闪烁”跳转。 - URL 是单一事实来源: 不要过度依赖
state传参,尽量将状态同步到 URL 上,增强页面的可分享性和健壮性。
评论 (0)
请先登录后再发表评论