React интеграция

HERE Technologies предоставляет набор картографических сервисов, где ключевую роль в веб-разработке играет HERE Maps API for JavaScript. При интеграции с React основная сложность заключается не в инициализации карты, а в корректном управлении жизненным циклом компонентов, синхронизации DOM-узлов и предотвращении повторной инициализации SDK при каждом рендере.

React работает декларативно, тогда как HERE Maps API — императивный SDK, напрямую взаимодействующий с DOM. Это приводит к необходимости изоляции карты в пределах ref, контролируемого побочными эффектами через useEffect, а также к строгому управлению очисткой ресурсов.


Подключение библиотеки и подготовка окружения

Для работы с HERE Maps API в React чаще всего используется загрузка скрипта через CDN или установка через npm-обёртки (если используются сторонние пакеты). Базовый вариант предполагает подключение скриптов в index.html:

<script src="https://js.api.here.com/v3/3.1/mapsjs-core.js"></script>
<script src="https://js.api.here.com/v3/3.1/mapsjs-service.js"></script>
<script src="https://js.api.here.com/v3/3.1/mapsjs-ui.js"></script>
<script src="https://js.api.here.com/v3/3.1/mapsjs-mapevents.js"></script>
<link rel="stylesheet" href="https://js.api.here.com/v3/3.1/mapsjs-ui.css" />

Ключевой момент — порядок загрузки модулей. core должен быть загружен первым, затем сервисы, UI и события.

Альтернативный подход — динамическая загрузка скриптов внутри React-приложения, что особенно важно при SSR-окружениях (например, Next.js), где window недоступен на сервере.


Базовая инициализация карты в React-компоненте

Основной принцип интеграции — создание контейнера через useRef и инициализация карты внутри useEffect, чтобы гарантировать наличие DOM-узла.

import { useEffect, useRef } from "react";

const HERE_API_KEY = "YOUR_API_KEY";

export default function HereMap() {
  const mapRef = useRef(null);
  const mapInstance = useRef(null);

  useEffect(() => {
    if (!mapRef.current) return;

    const platform = new window.H.service.Platform({
      apikey: HERE_API_KEY,
    });

    const defaultLayers = platform.createDefaultLayers();

    const map = new window.H.Map(
      mapRef.current,
      defaultLayers.vector.normal.map,
      {
        center: { lat: 48.8566, lng: 2.3522 },
        zoom: 10,
        pixelRatio: window.devicePixelRatio || 1,
      }
    );

    const beh * avior = new window.H.mapevents.Behavior(
      new window.H.mapevents.MapEvents(map)
    );

    const ui = window.H.ui.UI.createDefault(map, defaultLayers);

    mapInstance.current = map;

    return () => {
      map.dispose();
    };
  }, []);

  return <div ref={mapRef} style={{ width: "100%", height: "500px" }} />;
}

Ключевой аспект заключается в том, что экземпляр карты сохраняется отдельно от React-рендера. Это предотвращает повторное создание карты при каждом обновлении компонента.


Управление жизненным циклом и очистка ресурсов

HERE Maps API создаёт внутренние обработчики событий, WebGL-контекст и DOM-слои. При отсутствии корректного уничтожения экземпляра возникают утечки памяти.

Основной метод очистки:

return () => {
  if (mapInstance.current) {
    mapInstance.current.dispose();
    mapInstance.current = null;
  }
};

Особенно важно вызывать dispose() при:

  • смене маршрута (React Router)
  • размонтировании компонента
  • переключении тем или слоёв карты

Добавление маркеров и объектов

Работа с объектами карты осуществляется через H.map.Marker. В React добавление маркеров также выполняется внутри useEffect, чтобы избежать повторного добавления.

useEffect(() => {
  if (!mapInstance.current) return;

  const marker = new window.H.map.Marker({
    lat: 48.8566,
    lng: 2.3522,
  });

  mapInstance.current.addObject(marker);

  return () => {
    mapInstance.current.removeObject(marker);
  };
}, []);

Паттерн управления объектами карты должен повторять React-логику: добавление при монтировании, удаление при размонтировании.


Реактивное обновление состояния карты

Часто требуется синхронизировать состояние React с картой: координаты, масштаб, выбранные объекты.

useEffect(() => {
  if (!mapInstance.current) return;

  mapInstance.current.setCenter({
    lat: props.lat,
    lng: props.lng,
  });

  mapInstance.current.setZoom(props.zoom);
}, [props.lat, props.lng, props.zoom]);

Важно избегать обратной зависимости: изменения карты не должны бесконечно триггерить React state без дебаунса или фильтрации событий.


Работа с событиями карты

HERE Maps API предоставляет событийную модель через mapevents. В React события должны регистрироваться и сниматься корректно.

useEffect(() => {
  if (!mapInstance.current) return;

  const map = mapInstance.current;

  const handleTap = (evt) => {
    const coord = map.screenToGeo(
      evt.currentPointer.viewportX,
      evt.currentPointer.viewportY
    );

    console.log(coord);
  };

  map.addEventListener("tap", handleTap);

  return () => {
    map.removeEventListener("tap", handleTap);
  };
}, []);

Особенность заключается в том, что события привязаны не к DOM, а к внутренней модели карты.


Кастомные React-хуки для HERE Maps

Для масштабируемых приложений целесообразно выделять логику карты в отдельные хуки.

import { useEffect, useRef } from "react";

export function useHereMap(center, zoom) {
  const mapRef = useRef(null);
  const instanceRef = useRef(null);

  useEffect(() => {
    if (!mapRef.current) return;

    const platform = new window.H.service.Platform({
      apikey: "YOUR_API_KEY",
    });

    const layers = platform.createDefaultLayers();

    instanceRef.current = new window.H.Map(
      mapRef.current,
      layers.vector.normal.map,
      { center, zoom }
    );

    return () => {
      instanceRef.current.dispose();
    };
  }, []);

  useEffect(() => {
    if (instanceRef.current) {
      instanceRef.current.setCenter(center);
      instanceRef.current.setZoom(zoom);
    }
  }, [center, zoom]);

  return { mapRef, map: instanceRef.current };
}

Такой подход отделяет инфраструктуру карты от UI-логики компонента.


Интеграция с TypeScript

При использовании TypeScript основная проблема — отсутствие строгих типов у глобального H объекта.

Можно определить минимальные типы:

declare global {
  interface Window {
    H: any;
  }
}

Более строгий подход предполагает описание интерфейсов для карты, маркеров и событий, однако из-за сложности SDK часто используется частичная типизация.


Оптимизация производительности

При работе с большим количеством объектов на карте возникают проблемы производительности WebGL.

Основные техники оптимизации:

  • кластеризация маркеров через H.clustering
  • ограничение частоты обновления состояния React
  • использование requestAnimationFrame для анимаций
  • минимизация вызовов setCenter и setZoom

Пример кластеризации:

const dataPoints = data.map(item => new window.H.clustering.DataPoint(
  item.lat,
  item.lng
));

const clusteringProvider = new window.H.clustering.Provider(dataPoints);

const layer = new window.H.map.layer.ObjectLayer(clusteringProvider);

mapInstance.current.addLayer(layer);

Работа в SSR (Next.js и аналогичные среды)

Поскольку HERE Maps API зависит от window, прямой импорт приводит к ошибкам на сервере.

Решение — динамический импорт:

useEffect(() => {
  if (typeof window === "undefined") return;

  import("@here/maps-api-for-javascript").then(() => {
    // инициализация карты
  });
}, []);

Также допустимо полностью отключать SSR для компонента:

export default dynamic(() => import("./HereMap"), { ssr: false });

Управление слоями и кастомизация

HERE Maps API позволяет переключать слои:

  • растровые
  • векторные
  • спутниковые
const layers = platform.createDefaultLayers();

map.setBaseLayer(layers.raster.satellite.map);

В React это часто связывается с состоянием UI:

useEffect(() => {
  if (!mapInstance.current) return;

  mapInstance.current.setBaseLayer(
    layers[props.layerType]
  );
}, [props.layerType]);

Частые архитектурные ошибки

В интеграции React и HERE Maps API часто встречаются следующие проблемы:

  • повторная инициализация карты при каждом рендере
  • отсутствие dispose() и утечки памяти
  • хранение экземпляра карты в state вместо ref
  • синхронизация React state с картой без контроля частоты обновлений
  • попытка декларативного управления картой без императивного слоя

Правильная архитектура предполагает жёсткое разделение: React управляет UI-состоянием, HERE Maps — визуализацией и геометрией.


Сценарии масштабируемой интеграции

В крупных приложениях карта рассматривается как отдельный подсистемный слой:

  • MapContainer (инициализация)
  • MapObjectsLayer (маршруты, маркеры)
  • MapControlsLayer (UI элементы)
  • MapServices (геокодинг, маршрутизация)

Такое разделение позволяет изолировать взаимодействие с HERE Maps API for JavaScript от бизнес-логики React-приложения и уменьшает связность компонентов.