react-use 的 useGeolocation:在 React 组件中追踪用户地理位置的传感器 Hook 实战指南
【免费下载链接】react-useReact Hooks — 👍项目地址: https://gitcode.com/gh_mirrors/re/react-use
导读
useGeolocation是 react-use 提供的传感器(Sensor)Hook 之一,用于在 React 组件中实时追踪用户设备的地理位置,并将坐标、精度、速度等地理信息以响应式 state 的形式暴露给组件。本文将以 docs/useGeolocation.md 为主线,结合 src/useGeolocation.ts 的源码实现,带你掌握它的基础用法、完整返回状态、PositionOptions配置参数、错误处理机制与底层原理,从而在位置签到、导航地图、附近推荐等场景中直接落地使用。
功能概览:什么是 useGeolocation
在 react-use 中,"Sensor Hooks" 专门用于监听某个外部接口的变化,并强制组件携带最新状态重新渲染(参见 docs/Sensors.md)。useGeolocation正是这一类 Hook:它封装了浏览器原生的 Geolocation API(navigator.geolocation),持续追踪用户的地理位置。
根据 README.md 的描述,它的定位是 "tracks geo location state of user's device",即追踪用户设备的地理位置状态。它接收可选的 PositionOptions 配置(enableHighAccuracy、timeout、maximumAge),并把位置信息包装成统一的响应式状态对象返回。
作为传感器 Hook,它的核心价值在于:
- 开箱即用:无需手动管理
getCurrentPosition/watchPosition/clearWatch的生命周期; - 响应式状态:位置变化自动触发组件重渲染,可直接用于渲染或接入业务逻辑;
- 统一的错误处理:将定位失败原因(权限拒绝、超时、位置不可用)收敛为 state 中的
error字段。
快速上手:基础用法
useGeolocation的用法非常简洁,无需任何参数即可开始追踪:
import {useGeolocation} from 'react-use'; const Demo = () => { const state = useGeolocation(); return ( <pre> {JSON.stringify(state, null, 2)} </pre> ); };在 stories/useGeolocation.story.tsx 中,react-use 自带的 Storybook Demo 正是这样实现的——直接调用useGeolocation(),并将返回的state序列化渲染到<pre>标签中,方便直观地查看实时地理位置数据。
useGeolocation通过 src/index.ts 从库的根入口导出(export { default as useGeolocation } from './useGeolocation'),因此既可以按需单独引入,也可以与其他 Hook 一起使用命名导入:
// 按需引入(推荐,避免引入无关依赖) import useGeolocation from 'react-use/lib/useGeolocation'; // 命名导入(配合 tree shaking) import {useGeolocation} from 'react-use';返回状态详解:GeoLocationSensorState
useGeolocation返回一个状态对象,其完整类型定义在 src/useGeolocation.ts 中,名为GeoLocationSensorState:
interface GeoLocationSensorState { loading: boolean; accuracy: number | null; altitude: number | null; altitudeAccuracy: number | null; heading: number | null; latitude: number | null; longitude: number | null; speed: number | null; timestamp: number | null; error?: Error | IGeolocationPositionError; }各字段含义与来源如下表所示:
| 字段 | 类型 | 说明 |
|---|---|---|
loading | boolean | 是否正在获取位置。初始为true,首次定位成功或失败后变为false |
latitude | number \| null | 纬度,单位为度(十进制),范围 -90 到 90 |
longitude | number \| null | 经度,单位为度(十进制),范围 -180 到 180 |
accuracy | number \| null | 经纬度精度,单位为米 |
altitude | number \| null | 海拔高度,单位为米,设备不支持时为null |
altitudeAccuracy | number \| null | 海拔精度,单位为米,设备不支持时为null |
heading | number \| null | 设备移动方向(相对正北的顺时针角度,0~360 度),静止或设备不支持时为null |
speed | number \| null | 设备移动速度,单位为米/秒,设备不支持时为null |
timestamp | number \| null | 位置数据对应的时间戳 |
error | Error \| IGeolocationPositionError(可选) | 定位失败时的错误信息,成功时不存在该字段 |
初始状态
从源码可以看到,Hook 在创建时即初始化了默认状态(src/useGeolocation.ts):
const [state, setState] = useState<GeoLocationSensorState>({ loading: true, accuracy: null, altitude: null, altitudeAccuracy: null, heading: null, latitude: null, longitude: null, speed: null, timestamp: Date.now(), });也就是说,组件首次渲染时loading为true,各坐标字段为null,timestamp为调用时的当前时间。你可以基于loading字段渲染加载态,例如:
const Demo = () => { const state = useGeolocation(); if (state.loading) { return <div>正在获取位置…</div>; } if (state.error) { return <div>定位失败:{state.error.message}</div>; } return ( <div> 纬度:{state.latitude},经度:{state.longitude}(精度 ±{state.accuracy} 米) </div> ); };位置更新回调
当浏览器返回定位结果时,源码通过onEvent回调将event.coords中的各坐标字段逐一映射进 state,并把loading置为false(src/useGeolocation.ts):
const onEvent = (event: any) => { if (mounted) { setState({ loading: false, accuracy: event.coords.accuracy, altitude: event.coords.altitude, altitudeAccuracy: event.coords.altitudeAccuracy, heading: event.coords.heading, latitude: event.coords.latitude, longitude: event.coords.longitude, speed: event.coords.speed, timestamp: event.timestamp, }); } };注意event.timestamp取自 Geolocation API 返回事件本身的时间戳,而非Date.now(),这样能准确反映设备定位完成的时刻。
配置参数:PositionOptions
useGeolocation的完整函数签名(见 docs/useGeolocation.md 的 Reference 部分)为:
useGeolocation(options: PositionOptions)其中options是浏览器标准 Geolocation API 定义的PositionOptions对象,三个可选字段及建议取值如下:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enableHighAccuracy | boolean | false | 是否请求高精度定位。设为true时精度更高,但可能更耗电、响应更慢 |
timeout | number | Infinity(不超时) | 允许设备返回位置的最长等待时间,单位为毫秒。超时后触发TIMEOUT错误 |
maximumAge | number | 0 | 允许复用缓存位置的最大时限,单位为毫秒。设为0表示始终获取新位置,设为较大值可减少定位等待 |
实际使用时,可以按场景传入配置,例如在高精度需求的导航场景中:
import {useGeolocation} from 'react-use'; const App = () => { const state = useGeolocation({ enableHighAccuracy: true, timeout: 5000, maximumAge: 0, }); // ... };从源码可见,这份options会被原样透传给navigator.geolocation.getCurrentPosition和navigator.geolocation.watchPosition(src/useGeolocation.ts),因此其行为完全遵循浏览器标准,无需在 Hook 层做额外转换。
源码剖析:定位、监听与清理的完整链路
src/useGeolocation.ts 的实现非常精简,整体逻辑分为三步,都集中在useEffect中:
useEffect(() => { navigator.geolocation.getCurrentPosition(onEvent, onEventError, options); watchId = navigator.geolocation.watchPosition(onEvent, onEventError, options); return () => { mounted = false; navigator.geolocation.clearWatch(watchId); }; }, []);1. 一次性获取当前位置
navigator.geolocation.getCurrentPosition(onEvent, onEventError, options)用于在 Hook 挂载时立即获取一次当前位置。若成功,通过onEvent更新 state;若失败,通过onEventError写入错误。
2. 持续监听位置变化
navigator.geolocation.watchPosition(onEvent, onEventError, options)注册一个持续监听器,设备位置一旦变化就会再次触发onEvent,从而驱动组件实时重渲染。这正是"传感器 Hook"语义的体现——位置变化被转化为 React 状态更新。
3. 卸载时清理
useEffect的清理函数做了两件事:
- 将
mounted置为false,防止组件卸载后异步回调仍去调用setState,避免"在已卸载组件上更新状态"的警告与内存问题; - 调用
navigator.geolocation.clearWatch(watchId)注销监听器,释放浏览器资源。
值得注意的是,useEffect的依赖数组为空([]),意味着定位监听只在组件挂载时注册一次、卸载时清理一次,options变化不会导致监听重建。
错误处理:兼容两种错误类型
源码在文件头部专门定义了一个兼容接口IGeolocationPositionError(src/useGeolocation.ts):
export interface IGeolocationPositionError { readonly code: number; readonly message: string; readonly PERMISSION_DENIED: number; readonly POSITION_UNAVAILABLE: number; readonly TIMEOUT: number; }正如源码注释所说明的:由于PositionError在 TypeScript 4.1.x 中被重命名为GeolocationPositionError,为了在不同 TypeScript 版本下编译不报错,react-use 自己定义了兼容接口。错误回调onEventError的逻辑是:
const onEventError = (error: IGeolocationPositionError) => mounted && setState((oldState) => ({ ...oldState, loading: false, error }));它保留原有字段、将loading置为false并写入error。开发者可以根据error.code区分失败原因:
error.PERMISSION_DENIED(1):用户拒绝了位置权限;error.POSITION_UNAVAILABLE(2):无法获取位置(如设备定位模块不可用);error.TIMEOUT(3):定位超时(通常与options.timeout配合出现)。
实战应用场景与注意事项
典型应用场景
- 位置展示:将
latitude/longitude传给地图组件(如自定义地图 marker 定位); - 附近推荐:结合
accuracy过滤不可靠坐标,实现"附近门店/好友"类功能; - 运动轨迹:利用
speed、heading展示实时速度与方向; - 海拔相关:借助
altitude实现登山、骑行等户外场景的垂直信息展示。
注意事项
- 必须开启权限:Geolocation API 要求页面在安全上下文(HTTPS)中运行,且用户需授权位置权限;被拒绝后
error.code为PERMISSION_DENIED,UI 上应给出友好提示与引导。 - 首帧为加载态:
loading初始为true,坐标字段为null,渲染前务必做好判空处理,避免对null直接取属性。 - 高精度有代价:
enableHighAccuracy: true会增大耗电与定位延迟,非必要场景建议保持默认。 - 监听生命周期由 Hook 托管:组件卸载时监听器会被自动
clearWatch,无需手动处理。
相关资源
- 本文主体文档:docs/useGeolocation.md
- 完整源码实现:src/useGeolocation.ts
- 库入口导出:src/index.ts
- 交互式 Demo:stories/useGeolocation.story.tsx
- 传感器 Hook 总览:docs/Sensors.md
- 安装与按需引入方式:docs/Usage.md
【免费下载链接】react-useReact Hooks — 👍项目地址: https://gitcode.com/gh_mirrors/re/react-use
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考