react-use 的 useLocation:在 React 中响应式追踪浏览器 location 的完整指南
2026/9/19 3:45:16 网站建设 项目流程

react-use 的 useLocation:在 React 中响应式追踪浏览器 location 的完整指南

【免费下载链接】react-useReact Hooks — 👍项目地址: https://gitcode.com/gh_mirrors/re/react-use

useLocation是 react-use 提供的一个Sensor(传感器)类 Hook,用于在 React 组件中响应式地跟踪浏览器当前的location信息。本文将以其官方文档 docs/useLocation.md 为核心,结合 src/useLocation.ts 的源码实现,讲解它的返回结构、底层事件机制、SSR 兼容策略以及在实际项目中的使用方式,帮助你快速掌握"URL 变化即组件重新渲染"的响应式编程姿势。

一、useLocation 是什么

useLocation是一个 React sensor hook,用于跟踪浏览器的位置信息。它的核心价值在于:当浏览器的 URL 发生变化时(无论是通过前进/后退按钮,还是通过history.pushState/history.replaceState编程式跳转),Hook 内部的状态会自动更新,并触发使用它的组件重新渲染,从而拿到最新的location快照。

在 react-use 的 Hook 分类中,它与useHashuseSearchParamuseMedia等同属于 Sensors 家族,官方文档位于 docs/useLocation.md。需要注意的是,官方文档描述其为 "tracks brower's location",并且特别提示:在 Internet Explorer 中需要先安装 polyfill(对应 react-use 仓库 issue #73 中讨论的Event构造器缺失问题,下文会展开说明)。

二、快速上手:基础用法

1. 引入与最小示例

从 react-use 中引入useLocation,调用它即可拿到一个描述当前页面位置的对象:

import {useLocation} from 'react-use'; const Demo = () => { const state = useLocation(); return ( <pre> {JSON.stringify(state, null, 2)} </pre> ); };

这是官方文档给出的原始示例:useLocation()不需要任何参数,返回的对象可以直接序列化为 JSON 展示。当 URL 变化时,state会自动刷新,<pre>中的内容也会随之更新。

2. 返回对象的完整结构

从源码 src/useLocation.ts 中的LocationSensorState接口可以看到,返回值包含以下字段:

字段类型含义
triggerstring本次状态更新的触发来源,取值为'load''popstate''pushstate''replacestate'
stateany当前history.state,即pushState/replaceState时写入的自定义状态数据
lengthnumber当前会话的 history 记录条数
hashstringURL 中#之后的部分(含#
hoststring主机名和端口号,如example.com:8080
hostnamestring主机名,如example.com
hrefstring完整的 URL
originstring协议 + 主机 + 端口,如https://example.com:8080
pathnamestring路径部分,如/docs/useLocation
portstring端口号,如8080(默认端口为空字符串)
protocolstring协议,如https:
searchstring查询字符串,如?edit=123(含?

其中hashhosthostnamehreforiginpathnameportprotocolsearch直接来自window.location,而statelength来自window.history,这一点在源码的buildState函数中有清晰的体现(src/useLocation.ts)。

注意:trigger是 react-use 为该 Hook 额外引入的字段,用来标识状态更新的来源事件,这在排查"是什么操作触发了重新渲染"时非常有用。

三、底层原理:源码级解析

1. 双实现分支:浏览器端与服务器端

useLocation的源码末尾通过条件判断决定导出哪一个实现(src/useLocation.ts):

const hasEventConstructor = typeof Event === 'function'; export default isBrowser && hasEventConstructor ? useLocationBrowser : useLocationServer;

也就是说:

  • 浏览器环境且Event构造器可用→ 使用useLocationBrowser(完整功能);
  • 非浏览器环境(SSR)或缺少Event构造器的旧浏览器→ 回退到useLocationServer

useLocationServer的实现非常简单(src/useLocation.ts),只返回一个静态对象,避免在服务端渲染时访问window导致报错:

const useLocationServer = (): LocationSensorState => ({ trigger: 'load', length: 1, });

这正是 react-use 保证 SSR 安全(isomorphic)的一贯做法,isBrowser的定义在 src/misc/util.ts 中:typeof window !== 'undefined'

2. 核心机制:补丁 pushState / replaceState + 监听三类事件

浏览器端的useLocationBrowser做了两件事(src/useLocation.ts):

第一,初始化状态。通过useState(buildState('load'))在组件挂载时读取一次当前的window.historywindow.location快照,trigger标记为'load'

第二,在useEffect中注册三个事件监听器:

const onPopstate = () => setState(buildState('popstate')); const onPushstate = () => setState(buildState('pushstate')); const onReplacestate = () => setState(buildState('replacestate')); on(window, 'popstate', onPopstate); on(window, 'pushstate', onPushstate); on(window, 'replacestate', onReplacestate); return () => { off(window, 'popstate', onPopstate); off(window, 'pushstate', onPushstate); off(window, 'replacestate', onReplacestate); };

这里的关键点在于:popstate是浏览器原生事件(前进/后退时触发),但pushstatereplacestate并不是原生事件——它们是 react-use 在模块加载时通过patchHistoryMethod手动补丁出来的(src/useLocation.ts):

const patchHistoryMethod = (method) => { const history = window.history; const original = history[method]; history[method] = function (state) { const result = original.apply(this, arguments); const event = new Event(method.toLowerCase()); (event as any).state = state; window.dispatchEvent(event); return result; }; }; if (isBrowser) { patchHistoryMethod('pushState'); patchHistoryMethod('replaceState'); }

这段代码在模块顶层(isBrowser为真时)对window.history.pushStatewindow.history.replaceState做了包装:调用原始方法完成跳转后,再手动dispatchEvent一个同名的自定义事件(pushstate/replacestate),并把传入的state挂到事件的state属性上。这样一来,无论用户是通过浏览器前进/后退,还是通过pushState/replaceState编程式跳转,useLocation都能感知到并刷新状态——这正是该 Hook 能"全方位"跟踪 URL 变化的秘密所在。

事件监听的on/off工具函数来自 src/misc/util.ts,内部对addEventListener/removeEventListener做了空值保护,组件卸载时useEffect的清理函数会移除全部监听器,避免内存泄漏。

3. 为什么 IE 需要 polyfill

补丁代码中使用了new Event(...)构造器,而IE 不支持Event构造器(它只支持document.createEvent)。因此官方文档特别提示:在 Internet Explorer 中使用useLocation需要先安装 polyfill。同时,源码中的hasEventConstructor检查也保证了在缺少Event构造器的环境中,Hook 会安全回退到useLocationServer,不至于直接崩溃。

四、在真实项目中驱动 URL 变化

1. 通过 pushState 触发更新

history.pushState是单页应用中最常见的"无刷新跳转"方式。结合上面的源码分析,调用pushState会触发 react-use 补丁出的pushstate事件,进而刷新 Hook 状态。仓库中的 Storybook 演示 stories/useLocation.story.tsx 给出了一个可直接运行的最小示例:

import { storiesOf } from '@storybook/react'; import * as React from 'react'; import { useLocation } from '../src'; const go = (page) => window.history.pushState({}, '', page); const Demo = () => { const state = useLocation(); return ( <div> <button onClick={() => go('page-1')}>Page 1</button> <button onClick={() => go('page-2')}>Page 2</button> <pre>{JSON.stringify(state, null, 2)}</pre> </div> ); };

点击 "Page 1" / "Page 2" 两个按钮后,URL 的pathname会变为.../page-1.../page-2,而state.trigger会变为'pushstate'<pre>中的 JSON 也会实时刷新。这个 Story 还展示了useLocationShowDocs组件的配合方式,说明文档与可运行 Demo 是一一对应的。

2. 在业务组件中的典型用法

在实际应用中,你通常需要根据 URL 的变化执行副作用,例如根据路由参数重新拉取数据、更新面包屑或高亮导航菜单。可以配合useEffect使用:

import {useLocation, useEffectOnce} from 'react-use'; const PageTracker = () => { const location = useLocation(); useEffectOnce(() => { // 首次挂载时的逻辑(可选) }); // 每次 URL 变化都会触发重新渲染,从而执行这里的副作用 // 例如上报埋点:trackPageView(location.href); return ( <div> <p>当前路径:{location.pathname}</p> <p>查询参数:{location.search}</p> <p>Hash:{location.hash}</p> </div> ); };

由于useLocation返回的是一个包含 12 个字段的新对象,URL 变化时该对象必然是新引用,因此直接把它放进useEffect的依赖数组即可可靠地触发副作用。

3. 与 useHash、useSearchParam 的分工

react-use 还提供了两个更聚焦的姊妹 Hook:

  • useHash:只跟踪window.location.hash
  • useSearchParam:只跟踪 URL 的某个查询参数(如?edit=123)。

它们内部同样依赖history.pushState驱动的更新机制(参见 docs/useSearchParam.md 中的按钮示例)。当你只需要 hash 或单个查询参数时,优先使用它们可以获得更精简的返回值;而useLocation适合需要完整 location 快照的场景。

4. 已知限制:与 Hash 路由的冲突

使用useLocation(以及useSearchParam)时需要注意:如果项目使用了 Hash 路由(如react-routerHashRouter),浏览器会把?search参数视为 URL hash 的一部分,window.location.search将无法读到它们。这一点在 docs/useSearchParam.md 的 "Caveats/Gotchas" 一节有明确警示。因此,若你的应用基于 Hash 路由,useLocation返回的search字段可能为空,需要改用从hash中解析参数。

五、SSR 与兼容性要点

  1. SSR 安全:非浏览器环境下自动回退到useLocationServer,返回{ trigger: 'load', length: 1 },不会因为访问window而抛错,可以放心用于 Next.js 等服务端渲染场景。
  2. IE 兼容:IE 下需要为Event构造器安装 polyfill,否则会走useLocationServer分支,失去实时跟踪能力。
  3. 自动清理:组件卸载时useEffect的清理函数会移除popstatepushstatereplacestate三个监听器,无需手动管理。

六、结语

useLocation以极简的 API(零参数调用)封装了浏览器history+location的完整状态跟踪能力:通过补丁pushState/replaceState并监听popstate事件,它把"URL 变化"这一浏览器级事件无缝转化为 React 状态更新,同时借助双实现分支保证了 SSR 与旧浏览器的可用性。无论你是要实现路由联动组件、URL 驱动的数据请求,还是想深入理解 react-use 的 sensor 类 Hook 设计思路,docs/useLocation.md 与 src/useLocation.ts 都是值得反复研读的范本。

【免费下载链接】react-useReact Hooks — 👍项目地址: https://gitcode.com/gh_mirrors/re/react-use

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询