В современной разработке пользовательских интерфейсов хуки позволяют инкапсулировать логику работы с компонентами и повторно использовать её в разных частях приложения. При интеграции HERE Maps API в приложения на JavaScript, особенно при использовании React, хуки становятся важным инструментом управления жизненным циклом карты, обработкой событий, состоянием объектов и взаимодействием с API.
Под хуками для карт обычно понимаются пользовательские (custom) React-хуки, внутри которых сосредоточена логика создания карты, управления слоями, маркерами, маршрутами, геолокацией и другими функциями HERE Maps.
Основные преимущества такого подхода:
Одной из наиболее распространённых задач является инициализация карты.
Без выделения логики в хук код часто становится громоздким:
useEffect(() => {
const platform = new H.service.Platform({
apikey: API_KEY
});
const layers = platform.createDefaultLayers();
const map = new H.Map(
mapRef.current,
layers.vector.normal.map,
{
zoom: 10,
center: { lat: 55.751244, lng: 37.618423 }
}
);
return () => {
map.dispose();
};
}, []);
Подобный код приходится повторять в разных компонентах.
Более удобным решением становится создание специализированного хука.
import { useEffect, useRef } from "react";
export function useHereMap(containerRef, options) {
const mapRef = useRef(null);
useEffect(() => {
const platform = new H.service.Platform({
apikey: options.apiKey
});
const layers = platform.createDefaultLayers();
const map = new H.Map(
containerRef.current,
layers.vector.normal.map,
{
center: options.center,
zoom: options.zoom
}
);
mapRef.current = map;
return () => {
map.dispose();
};
}, []);
return mapRef;
}
Использование:
const mapContainer = useRef(null);
const map = useHereMap(mapContainer, {
apiKey: API_KEY,
center: { lat: 55.75, lng: 37.61 },
zoom: 12
});
В результате компонент отвечает только за отображение интерфейса, а вся логика создания карты вынесена в отдельный модуль.
Во многих проектах требуется получать текущий экземпляр карты из разных компонентов.
Для этого можно использовать отдельный хук-контейнер.
import { createContext, useContext } from "react";
const MapContext = createContext(null);
export function useMap() {
return useContext(MapContext);
}
Провайдер:
<MapContext.Provider value={map}>
{children}
</MapContext.Provider>
Получение карты:
const map = useMap();
map.setZoom(15);
Подобная архитектура избавляет от передачи объекта карты через множество уровней компонентов.
Изменение центра карты является одной из самых частых операций.
Создание специального хука упрощает управление этой функциональностью.
import { useCallback } from "react";
export function useMapCenter(map) {
const setCenter = useCallback((lat, lng) => {
if (!map) return;
map.setCenter({ lat, lng });
}, [map]);
return { setCenter };
}
Использование:
const { setCenter } = useMapCenter(map);
setCenter(48.8566, 2.3522);
Преимущество заключается в том, что вся логика изменения центра карты сосредоточена в одном месте.
Работа с масштабированием также часто выносится в отдельный слой абстракции.
import { useCallback } from "react";
export function useZoom(map) {
const zoomIn = useCallback(() => {
map.setZoom(map.getZoom() + 1);
}, [map]);
const zoomOut = useCallback(() => {
map.setZoom(map.getZoom() - 1);
}, [map]);
return {
zoomIn,
zoomOut
};
}
Пример:
const { zoomIn, zoomOut } = useZoom(map);
zoomIn();
zoomOut();
Такой подход делает интерфейс независимым от конкретной реализации HERE Maps API.
Добавление и удаление маркеров встречается практически в каждом картографическом приложении.
Хук может инкапсулировать всю связанную логику.
import { useCallback } from "react";
export function useMarkers(map) {
const addMarker = useCallback((position) => {
const marker = new H.map.Marker(position);
map.addObject(marker);
return marker;
}, [map]);
const removeMarker = useCallback((marker) => {
map.removeObject(marker);
}, [map]);
return {
addMarker,
removeMarker
};
}
Использование:
const { addMarker } = useMarkers(map);
addMarker({
lat: 55.75,
lng: 37.61
});
При отображении большого количества объектов важно централизованно управлять их жизненным циклом.
import { useEffect } from "react";
export function useMarkerCollection(map, points) {
useEffect(() => {
if (!map) return;
const markers = points.map(point => {
const marker = new H.map.Marker(point);
map.addObject(marker);
return marker;
});
return () => {
markers.forEach(marker => {
map.removeObject(marker);
});
};
}, [map, points]);
}
Пример:
useMarkerCollection(map, locations);
При изменении массива старые маркеры автоматически удаляются и создаются новые.
События карты удобно инкапсулировать внутри отдельных хуков.
import { useEffect } from "react";
export function useMapClick(map, callback) {
useEffect(() => {
if (!map) return;
const handler = (event) => {
const coord = map.screenToGeo(
event.currentPointer.viewportX,
event.currentPointer.viewportY
);
callback(coord);
};
map.addEventListener("tap", handler);
return () => {
map.removeEventListener("tap", handler);
};
}, [map, callback]);
}
Использование:
useMapClick(map, (coord) => {
console.log(coord);
});
HERE Maps часто используется вместе с браузерной геолокацией.
import { useState, useEffect } from "react";
export function useGeolocation() {
const [position, setPosition] = useState(null);
useEffect(() => {
navigator.geolocation.getCurrentPosition(
(result) => {
setPosition({
lat: result.coords.latitude,
lng: result.coords.longitude
});
}
);
}, []);
return position;
}
Использование:
const location = useGeolocation();
Полученные координаты могут использоваться для центрирования карты или построения маршрутов.
Для приложений навигации требуется постоянное обновление координат.
import { useEffect, useState } from "react";
export function useWatchPosition() {
const [position, setPosition] = useState(null);
useEffect(() => {
const watchId =
navigator.geolocation.watchPosition(
(result) => {
setPosition({
lat: result.coords.latitude,
lng: result.coords.longitude
});
}
);
return () => {
navigator.geolocation.clearWatch(watchId);
};
}, []);
return position;
}
Такой подход позволяет автоматически обновлять положение пользователя на карте.
Построение маршрутов является одной из ключевых возможностей HERE Maps.
Хук позволяет скрыть детали взаимодействия с Routing API.
import { useCallback } from "react";
export function useRouting(platform, map) {
const calculateRoute = useCallback(
async (origin, destination) => {
const router =
platform.getRoutingService(null, 8);
const route = await router.calculateRoute({
transportMode: "car",
origin,
destination,
return: "polyline"
});
return route;
},
[platform]
);
return {
calculateRoute
};
}
Использование:
const { calculateRoute } =
useRouting(platform, map);
Бизнес-логика маршрутизации полностью изолируется внутри специализированного хука.
После получения маршрута требуется отобразить его на карте.
import { useCallback } from "react";
export function useRouteRenderer(map) {
const drawRoute = useCallback((lineString) => {
const routeLine = new H.map.Polyline(
lineString
);
map.addObject(routeLine);
return routeLine;
}, [map]);
return { drawRoute };
}
Пример использования:
const { drawRoute } =
useRouteRenderer(map);
При большом количестве элементов полезно централизованно управлять объектами.
import { useRef } from "react";
export function useMapObjects(map) {
const objects = useRef([]);
const addObject = (object) => {
map.addObject(object);
objects.current.push(object);
};
const clearObjects = () => {
objects.current.forEach(object => {
map.removeObject(object);
});
objects.current = [];
};
return {
addObject,
clearObjects
};
}
Такой механизм особенно полезен при динамическом обновлении данных.
После добавления объектов часто требуется показать их все одновременно.
import { useCallback } from "react";
export function useFitBounds(map) {
const fitObjects = useCallback(() => {
const bounds =
map.getObjects()[0]?.getBoundingBox();
if (bounds) {
map.getViewModel()
.setLookAtData({
bounds
});
}
}, [map]);
return { fitObjects };
}
Использование:
const { fitObjects } =
useFitBounds(map);
fitObjects();
HERE Maps предоставляет различные типы картографических слоёв.
Для удобного управления можно создать отдельный хук.
import { useState } from "react";
export function useMapLayers(map, layers) {
const [currentLayer, setCurrentLayer] =
useState("normal");
const changeLayer = (name) => {
map.setBaseLayer(layers[name]);
setCurrentLayer(name);
};
return {
currentLayer,
changeLayer
};
}
Пример:
changeLayer("satellite");
Элементы управления HERE Maps могут динамически включаться и отключаться.
import { useCallback } from "react";
export function useMapUI(ui) {
const hideControls = useCallback(() => {
ui.getControl("zoom")
?.setVisibility(false);
}, [ui]);
const showControls = useCallback(() => {
ui.getControl("zoom")
?.setVisibility(true);
}, [ui]);
return {
hideControls,
showControls
};
}
Подобный подход облегчает создание адаптивных интерфейсов.
Крупные проекты редко ограничиваются одним хуком. Обычно используется композиция нескольких независимых модулей.
function MapContainer() {
const mapRef = useRef(null);
const map = useHereMap(
mapRef,
config
);
const { addMarker } =
useMarkers(map.current);
const { zoomIn } =
useZoom(map.current);
const userPosition =
useGeolocation();
useEffect(() => {
if (userPosition) {
addMarker(userPosition);
}
}, [userPosition]);
return (
<div
ref={mapRef}
style={{
width: "100%",
height: "600px"
}}
/>
);
}
Композиция хуков позволяет формировать сложную картографическую функциональность из небольших независимых блоков. Каждая часть отвечает только за одну задачу: инициализацию карты, управление масштабом, отображение маршрутов, работу с геолокацией или обработку событий. Такой подход делает приложения на основе HERE Maps API более модульными, расширяемыми и удобными для сопровождения.