尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

react-router 中的 useLocation Hook:读取并响应当前 Location 的完整指南

react-router 中的 useLocation Hook:读取并响应当前 Location 的完整指南 react-router 中的 useLocation Hook读取并响应当前 Location 的完整指南【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-routeruseLocation是 react-router 提供的一个核心 Hook它向组件返回当前路由位置Location 对象并在路由发生跳转时让组件重新渲染从而适合驱动基于 URL 变化的副作用。本指南以 useLocation.md 文档为骨架结合仓库内 Hook 源码hooks.tsx、Location 类型定义history.ts与测试用例展开帮助你在 framework框架、data数据路由与 declarative声明式三种模式下正确使用它完成页面统计、URL 联动状态等实战需求。一、什么是 useLocation在 react-router 中URL 的解析结果被抽象成一个不可变的 Location 对象。useLocation就是一个让你在任何处于 Router 上下文内的组件中读取当前这份 Location 的快照的 Hook。useLocation()may be used only in the context of aRoutercomponent.这句错误信息来自 hooks.tsx 中的invariant校验Hook 在调用时会先通过useInRouterContext()检查是否处于 Router 上下文内因此它必须被用在与路由树相关联的组件里例如被Routes渲染出的页面组件、布局组件或其任意子孙组件。从源码实现看它的全部逻辑非常精简export function useLocation(): Location { invariant( useInRouterContext(), useLocation() may be used only in the context of a Router component., ); return React.useContext(LocationContext).location; }也就是说Hook 的真实作用是React.useContext(LocationContext).location——从 React 的LocationContext上下文中取回路由当前维护的 location。这也解释了它为什么只读、且具有响应式Location 一旦变化使用它的组件就会自动重新渲染。适用于全部三种运行模式原文档在其元信息中标明该 Hook 的支持范围是[MODES: framework, data, declarative]意味着它同时适用于declarative 声明式模式配合BrowserRouter、MemoryRouter等声明式 Router 使用data 数据路由模式配合createBrowserRouterRouterProvider使用framework 框架模式配合 Vite 插件与文件路由约定使用参见 modes.md。无论哪种模式Router 都会在内部把当前 location 注入LocationContextuseLocation的读取方式保持一致。二、返回的 Location 对象长什么样useLocation的签名十分简单function useLocation(): Location它没有任何参数返回当前 Location 对象。Location类型的定义位于 packages/react-router/lib/router/history.ts其完整结构如下export interface LocationState any extends Path { /** 与该 location 关联的任意数据如 navigate 时传入的 state */ state: State; /** 与该 location 关联的唯一字符串可用于在 localStorage 等存储中安全地存取数据 * 注意初始 location 的 key 恒为 default */ key: string; /** URL 栏中显示的被掩蔽maskedlocation与路由实际操作的 URL 不同 */ mask?: Path; }其中父接口Path包含三个 URL 的组成部分history.ts字段说明示例pathname以/开头的 URL 路径名/projects/12search以?开头的查询字符串无查询时为空字符串?page2hash以#开头的片段标识符无片段时为空字符串#commentsstate导航时随 location 一起写入的任意状态数据{ from: /login }key该 history 条目的唯一标识初始条目固定为defaultdefaultmask仅在 URL 被掩蔽如不更新地址栏的导航时存在的可选字段undefined在测试目录 packages/react-router/tests/useLocation-test.tsx 中可以找到大量针对这些字段的断言例如验证 pathname/search/hash 与路由跳转结果一一对应验证 state 在 PUSH/REPLACE 与浏览器前进后退后的保留情况。三、官方推荐用法在 location 变化时执行副作用useLocation最常见的诉求是——当用户导航到新页面时执行某些副作用。由于 location 引用在每次导航发生时都会更新把它放进useEffect的依赖数组即可保证只在 URL 真正改变时触发回调而不是在组件每次无关渲染时都重复执行。原文档给出的官方示例是配合第三方统计Google Analytics发送页面浏览事件import * as React from react import { useLocation } from react-router function SomeComponent() { let location useLocation() React.useEffect(() { // Google Analytics ga(send, pageview) }, [location]); return ( // ... ); }在这个例子中useLocation()返回包含当前 pathname、search、hash 等信息的 Location 对象useEffect以[location]为依赖当用户从一个路由跳到另一个路由时location 引用变化回调随即执行一次由于回调里读取了最新的location上报的统计内容与当前页面 URL 保持一致。如果你想更精确地只在某个字段例如 pathname变化时触发逻辑也可以把依赖从整个对象换成location.pathname、location.search、location.key等具体字段从而过滤掉你并不关心的 URL 部分的变化。关于对象引用的稳定性的一个重要细节值得说明的是location 的引用不是每次渲染都新建的。在底层Router组件中location 快照被useMemo以[basename, pathname, search, hash, state, key, navigationType, mask]为依赖缓存参见 components.tsx。也就是说只有当 pathname、search、hash、state、key、navigationType、mask 这些构成要素之一真正变化时才会产生新的 Location 对象组件因父级 state 等无关原因重渲染时location 引用保持不变useEffect不会误触发。这保证了把整个location放进依赖数组既不会漏报也不会误报让副作用与 URL 的真实变化严格对齐。四、底层原理LocationContext 从哪来要理解useLocation的响应式来源需要看它读取的上下文是如何被填充的。上下文类型定义在 packages/react-router/lib/context.tsinterface LocationContextObject { location: Location; navigationType: NavigationType; } export const LocationContext React.createContextLocationContextObject( null!, ); LocationContext.displayName Location;可以看到同一个LocationContext里其实同时装了两份数据location当前 URL 位置和navigationType本次变更的类型取值为POP/PUSH/REPLACE。而往这个上下文填数据的是 Router 的核心渲染逻辑。在 components.tsx 中Router会把导航上下文含 basename 与 navigator和位置上下文嵌套提供给子树return ( NavigationContext.Provider value{navigationContext} LocationContext.Provider children{children} value{locationContext} / /NavigationContext.Provider );位置数据来源于浏览器 history / 内存 history 的监听与订阅每当发生 PUSH、REPLACE 或 POP前进/后退导航Router 都会更新内部 location 并触发重渲染进而让所有useLocation()的调用者拿到新值。这与数据路由模式RouterProvider与声明式模式BrowserRouter等的订阅链路是相通的唯一的差别仅在于 router 实例由谁创建与启动。声明式路由的更多细节可参见 Router.md数据路由可参见 RouterProvider.md。五、与相邻 Hook 的关系与选择useLocation属于 Hooks API 家族中读取位置信息的一类它和几个邻居共享同一个底层上下文掌握它们的分工能帮你写出更聚焦的代码Hook读取的数据典型用途useLocation整个 Locationpathname/search/hash/state/key/mask驱动页面级副作用、URL 相关状态同步useNavigationType本次导航的类型POP/PUSH/REPLACE区分首次加载、点击链接与前进/后退useInRouterContext是否处于 Router 上下文布尔值通用组件库中安全降级渲染useParams当前匹配的动态段参数从 URL 读取:id等参数useSearchParams查询字符串的读写封装读写?page等查询参数几个值得注意的源码级关联useInRouterContext()的实现就是React.useContext(LocationContext) ! nullhooks.tsx它和useLocation检查的是同一个上下文useNavigationType()与useLocation()共同读取LocationContexthooks.tsx因此如果组件需要知道用户是从哪里/以何种方式到达当前页例如区分首屏 POP 与后续导航可以在同一个位置顺便取得 navigationTypeuseMatch()内部同样调用了useLocation()来拿 pathname 再做模式匹配hooks.tsx说明位置信息是一切匹配类逻辑的输入源头。因此如果你的组件只是需要当前在哪个路由下做判断或副作用直接用useLocation如果还需要参数/查询串可组合useParams/useSearchParams而不必自己从 location 字符串中手工解析。六、典型实战场景1. 路由切换的埋点统计这是原文档给出的场景。更完善一点的版本可以结合location.pathname location.search并在首屏加载时也上报一次import * as React from react import { useLocation } from react-router function PageTracker() { const location useLocation(); React.useEffect(() { trackPageview(location.pathname location.search); }, [location.pathname, location.search]); return null; // 只做副作用不渲染 UI }把该组件放在路由树的顶层例如框架模式的 root.tsx 或声明式模式中Routes的外层即可全局生效。2. 页面离开前的确认与恢复利用 location 的变化配合sessionStorage/localStorage保存用户上一次访问的位置是文档提到key字段可用于安全地存取数据的典型场景初始 location 的 key 固定为default而后续每次 PUSH/REPLACE 都会生成新 key可以用来区分这是不是一次全新会话。3. 依赖 URL 的本地状态同步例如一个需要根据 URL 高亮菜单、切换 Tab、重置表单的组件可以订阅 location 并把这些派生状态同步进组件的 statefunction ResetOnRouteChange() { const location useLocation(); // 路由变化时清理旧页遗留的状态例如保存草稿用的 key React.useEffect(() { setDraftKey(location.key); }, [location.key]); return null; }4. 配合Link/useNavigate传参读取当通过Link to{{ pathname: /details, state: { from: list } }}或navigate(/details, { state })跳转时传入的任意 state 会原样出现在新页面的location.state上function DetailsPage() { const location useLocation(); const from location.state?.from ?? unknown; // ... }这是列表页记住来源、详情页返回原列表这类交互的基础相关跳转 API 见 useNavigate。七、使用注意事项必须在 Router 上下文中调用。否则会抛出错误useLocation() may be used only in the context of a Router component.hooks.tsx。该错误信息里附带了一条来自源码 TODO 的提示多数情况是组件树中存在两份不同版本的 react-router重复安装导致上下文断裂。location 是只读快照。不要尝试直接修改返回对象的字段来改变 URL正确做法是通过Link、useNavigate或navigate等 API 发起导航之后useLocation自然返回新值。依赖数组按需收敛。若副作用只关心路径变化建议用[location.pathname]而非[location]避免同一页面内 hash/search 的变动也触发逻辑反之若需要统计完整 URL含 query则把pathname search放进依赖。初始key是default。这是 Location 类型源码中明确注释的行为history.ts在判断用户是否通过站内导航而非直接刷新进入当前页时可以作为辅助信号但要意识到初始条目固定、后续条目才生成唯一 key。八、小结useLocation的 API 面极窄——无参数、返回当前 Location 对象但其背后是 react-router 位置订阅机制的对外出口Router 统一维护 location 并通过LocationContext见 context.ts注入组件树导航发生时生成新的 Location 快照并触发重渲染。用好它与useEffect的组合即可可靠地实现页面统计、URL 驱动的状态同步、跨页面传参等能力再配合 useNavigationType、useParams、useSearchParams 等相邻 Hook就能完整覆盖绝大多数依据 URL 驱动 UI的需求。想要在浏览器中动手验证其返回值与跳转行为可以基于仓库内测试 useLocation-test.tsx 的用例思路快速搭建实验或在 location.md 中了解 react-router 对 URL 位置这一核心概念的完整设计。【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表