Skip to content

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 ol

geoverseolreactreact-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
图层GvVectorLayerGvHeatLayerGvImageLayerGvCustomBaseLayerGvTrafficLayerGvClusterLayerGvSuperClusterLayerGvMassLayerGvNameLayer
要素GvMarkerGvCircleGvPolygonGvPolyline
交互GvDrawGvMeasureGvFeatureEditorGvTransform@experimental
控件 / 弹窗GvOverviewGvInfoWindow
轨迹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={ ... }(每次渲染新对象),切底图等无关重渲染也不会误重置几何。
  • 集合属性(如 GvMassLayergraphicsGvClusterLayermarkers):建议用 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/)。