Appearance
React 组件(@geoverse/react)
@geoverse/react 是 GeoVerse 的官方 React 组件层:把 core 的命令式 API 包装成声明式组件树与命令式 hooks,让你用 JSX 与 props/回调驱动地图。它是一层薄绑定——不复制任何地图逻辑,坐标 / 聚合 / 绘制等全部留在 core。
与 @geoverse/vue 组件清单、事件映射、受控约定 1:1 对齐;差异只在框架习惯(React 用 hooks / props+回调,Vue 用 composable / v-model)。
安装
shell
pnpm add @geoverse/react geoverse olgeoverse、ol、react、react-dom 均为 peerDependencies(与宿主共享实例)。要素变换组件 <GvTransform> 额外依赖可选 peer ol-ext,用到时再装 pnpm add ol-ext。支持 React 18 与 19。
快速上手(声明式)
tsx
import { useState } from 'react';
import { GvMap, GvVectorLayer, GvMarker, GvInfoWindow } from '@geoverse/react';
export default function MapDemo() {
// React 无 v-model,受控值用「值 + onChange」对
const [center, setCenter] = useState([118.18, 24.49]);
const [zoom, setZoom] = useState(12);
const [open, setOpen] = useState(false);
return (
<GvMap
style={{ height: 480 }}
base="gd-vec"
center={center}
zoom={zoom}
onCenterChange={setCenter}
onZoomChange={setZoom}
scaleLine
>
<GvVectorLayer>
<GvMarker
options={{ position: [118.18, 24.49], color: 'red', size: 8 }}
onClick={() => setOpen(true)}
/>
</GvVectorLayer>
<GvInfoWindow open={open} position={[118.18, 24.49]}>
<div className="popup">children 内容,经 createPortal 投递</div>
</GvInfoWindow>
</GvMap>
);
}<GvMap>在useEffect(浏览器侧)创建GMap,就绪后才渲染子组件并经MapContext下发;center/zoom受控:传入值 +onCenterChange/onZoomChange回写;底图base变化走switchBase;- 要素组件必须置于
<GvVectorLayer>内,否则抛出清晰错误。
命令式(hook)
需要完全控制时用 useGeoVerseMap(),在自己的容器 ref 上创建 / 销毁地图:
tsx
import { useRef } from 'react';
import { useGeoVerseMap } from '@geoverse/react';
export default function ImperativeMap() {
const el = useRef<HTMLDivElement>(null);
const map = useGeoVerseMap(
{ base: 'gd-vec', center: [118.18, 24.49], zoom: 12 },
el,
);
function locate() {
// map.current 是 GMap 实例,可直接调用任意 core API
map.current?.panTo({ zoom: 14 });
}
return <div ref={el} style={{ height: 480 }} />;
}在 <GvMap> 子树内则用 useMapContext() 取父地图(缺失时抛错)。
组件一览
| 类别 | 组件 |
|---|---|
| 容器 | GvMap |
| 图层 | GvVectorLayer、GvHeatLayer、GvImageLayer、GvCustomBaseLayer、GvTrafficLayer、GvClusterLayer、GvSuperClusterLayer、GvMassLayer、GvNameLayer |
| 要素 | GvMarker、GvCircle、GvPolygon、GvPolyline |
| 交互 | GvDraw、GvMeasure、GvFeatureEditor、GvTransform(@experimental) |
| 控件 / 弹窗 | GvOverview、GvInfoWindow |
| 轨迹 | GvPathSimplifier |
约定:图层 / 要素 / 工具组件的 options prop = 对应 core 类的构造选项;map / view 由组件自动注入;事件经回调 props 转发(如要素的 onClick、绘制的 onComplete、轨迹的 nodeClick)。每个组件都用 forwardRef 暴露底层实例。
实例访问
经 ref 拿到底层 core 实例(forwardRef + useImperativeHandle),或 <GvMap onReady> 回调:
tsx
import { useRef } from 'react';
import type { GMap } from 'geoverse';
import { GvMap } from '@geoverse/react';
const mapRef = useRef<GMap>(null);
<GvMap ref={mapRef} onReady={(map) => console.log('ready', map)} />;
// mapRef.current 就绪后即为 GMap 实例受控属性与响应式更新
<GvMap>的center/zoom受控:传入值变化 → 同步到视图;交互变化 →onCenterChange/onZoomChange回调(自行维护 state 形成受控环)。底图base变化触发switchBase(切投影并重投影既有图层/要素)。- 要素几何受控:
<GvMarker options>的position、<GvCircle>的center、<GvPolygon>/<GvPolyline>的path变化时,组件会按当前底图投影自动重投影并更新几何。坐标统一以 WGS-84 经纬度传入。- 几何更新按「值」比较触发(内部对坐标做 JSON 比较),因此即使用内联
options={ ... }(每次渲染新对象),切底图等无关重渲染也不会误重置几何。
- 几何更新按「值」比较触发(内部对坐标做 JSON 比较),因此即使用内联
- 集合属性(如
GvMassLayer的graphics、GvClusterLayer的markers):建议用useMemo保持稳定引用,避免每次渲染产生新数组导致抖动。 - 其余「构造即生效」的选项(样式、聚合
distance等)变化时不自动响应,需要时经组件ref取实例命令式更新,或改变key触发重建。
React 特有注意事项
- StrictMode 双挂载:React 18+ 在开发期
<StrictMode>下会「挂载→卸载→再挂载」,effect 跑两次。本库所有创建类副作用都在useEffect内新建实例、cleanup 完整销毁(destroy()/removeLayer+dispose()/unByKey+removeFeature),双挂载正确收敛为一张地图,不会叠加。你自己写 effect 创建实例时也请保证对称 cleanup。 - ol 实例不进 state:地图 / 图层 / 要素一律存
useRef(实例自身可变,放进useState既触发无谓 re-render 又无法反映内部变化);state 只放可序列化配置(center/zoom/样式 JSON)。 - 回调用 latest-ref:事件 handler(
onClick等)由组件内部以 latest-ref 模式只订阅一次,规避把 handler 放进依赖数组导致的「反复重订阅」或「闭包捕获旧值」。你无需useCallback包裹也不会重订阅。 - 依赖原始值而非引用:受控值(center/zoom)的同步按原始值比较,内联对象 props 不会误触发重建。
- SSR / Next.js:所有组件是客户端组件,入口已标注
'use client',实例化只在useEffect(浏览器侧)。Next.js App Router 下,使用地图的组件需'use client',或用next/dynamic+{ ssr: false }懒加载。 - 卸载顺序:React 保证子组件 cleanup 先于父,自动对称移除(要素先
removeFeature、图层后removeLayer、地图最后destroy());定时器 / 监听 / overlay 由 core 的destroy()完整回收。
在线示例:仓库
examples/react/(pnpm dev:examples→/react/)。