React-leaflet

React + Leaflet образуют типичный стек для создания интерактивных веб-карт, где логика управления состоянием и компонентная модель React соединяется с императивным API Leaflet через слой абстракции React Leaflet. Эта библиотека реализует декларативную обёртку над объектами карты, слоями, маркерами и событиями Leaflet, позволяя управлять картой через React-компоненты вместо прямого вызова методов API.

Leaflet построен на императивной модели: карта и её элементы создаются через вызовы вроде L.map(), L.tileLayer() и L.marker(). React, напротив, использует декларативную модель, где UI описывается как функция состояния.

React-leaflet выступает мостом между этими подходами:

  • React управляет жизненным циклом компонентов
  • Leaflet отвечает за рендеринг карты в DOM и работу с геометрией
  • React-leaflet синхронизирует props и Leaflet-объекты

Ключевая идея: React-компонент не рисует карту напрямую, а создаёт и обновляет Leaflet-слой.

Базовая структура карты

Основной контейнер — компонент MapContainer. Он инициализирует экземпляр карты Leaflet и предоставляет контекст для дочерних элементов.

import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";

export default function MapExample() {
  return (
    <MapContainer center={[55.751244, 37.618423]} zoom={10} style={{ height: "100vh" }}>
      <TileLayer
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      />
      <Marker position={[55.751244, 37.618423]}>
        <Popup>
          Центральная точка
        </Popup>
      </Marker>
    </MapContainer>
  );
}

MapContainer

Создаёт экземпляр карты Leaflet и связывает его с React-контекстом.

Основные параметры:

  • center — начальные координаты
  • zoom — уровень масштабирования
  • scrollWheelZoom — управление зумом колесом мыши
  • whenCreated — callback доступа к объекту карты

Слои тайлов (TileLayer)

Leaflet использует тайловую систему отображения карт. React-leaflet инкапсулирует её через компонент TileLayer.

<TileLayer
  url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
  attribution="© OpenStreetMap contributors"
/>

Особенности:

  • поддержка шаблонных URL {z}/{x}/{y}
  • возможность подключения коммерческих провайдеров карт
  • динамическая подгрузка тайлов при изменении viewport

Маркеры и всплывающие окна

Marker

Маркер представляет точечный объект на карте.

<Marker position={[51.505, -0.09]} />

Всплывающее окно привязывается к маркеру или любому слою.

<Marker position={[51.505, -0.09]}>
  <Popup>
    Лондон
  </Popup>
</Marker>

Popup автоматически управляет привязкой к слою через контекст React-leaflet.

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

React-leaflet не хранит состояние карты в React напрямую, но предоставляет доступ к экземпляру Leaflet через хуки.

useMap

import { useMap } from "react-leaflet";

function ZoomController() {
  const map = useMap();

  function zoomIn() {
    map.zoomIn();
  }

  return <button onCl ick={zoomIn}>Zoom In</button>;
}

Map — это прямой экземпляр Leaflet, позволяющий вызывать методы:

  • setView
  • flyTo
  • panTo
  • fitBounds

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

Leaflet поддерживает богатую систему событий, которую React-leaflet оборачивает в props.

<Marker
  position={[51.505, -0.09]}
  eventHandlers={{
    click: () => {
      console.log("Marker clicked");
    },
    mouseover: () => {
      console.log("Hover");
    }
  }}
/>

События проксируются напрямую из Leaflet-инстанса.

Динамическое обновление позиции

Изменение props автоматически приводит к обновлению слоя.

function MovingMarker({ position }) {
  return <Marker position={position} />;
}

При изменении position React-leaflet вызывает setLatLng внутри Leaflet.

Полилинии и полигоны

Polyline

import { Polyline } from "react-leaflet";

const path = [
  [51.505, -0.09],
  [51.51, -0.1],
  [51.52, -0.12]
];

<Polyline positions={path} color="blue" />

Polygon

import { Polygon } from "react-leaflet";

const triangle = [
  [51.51, -0.12],
  [51.5, -0.1],
  [51.52, -0.09]
];

<Polygon positions={triangle} />

Эти компоненты напрямую соответствуют геометрическим слоям Leaflet.

Работа с кастомными иконками

Leaflet использует объект Icon, который можно интегрировать через React-leaflet.

import L from "leaflet";
import { Marker } from "react-leaflet";

const icon = new L.Icon({
  iconUrl: "/marker.png",
  iconSize: [25, 41],
  iconAnchor: [12, 41]
});

<Marker position={[51.505, -0.09]} icon={icon} />

Контроль границ карты

import { useMap } from "react-leaflet";

function FitBounds({ bounds }) {
  const map = useMap();

  map.fitBounds(bounds);

  return null;
}

Используется для автоматического масштабирования под набор координат.

Слои и группы слоёв

LayerGroup

import { LayerGroup, Marker } from "react-leaflet";

<LayerGroup>
  <Marker position={[51.5, -0.09]} />
  <Marker position={[51.51, -0.1]} />
</LayerGroup>

Позволяет управлять группой объектов как единым слоем.

GeoJSON интеграция

Leaflet поддерживает GeoJSON напрямую.

import { GeoJSON } from "react-leaflet";

const data = {
  type: "Feature",
  geometry: {
    type: "Point",
    coordinates: [-0.09, 51.505]
  }
};

<GeoJSON data={data} />

GeoJSON позволяет отображать сложные географические структуры: полигоны, линии, точки.

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

React-leaflet синхронизирует lifecycle React с lifecycle Leaflet:

  • mount → создание Leaflet-объекта
  • update → вызов методов обновления
  • unmount → удаление слоя

Это предотвращает утечки памяти при частых перерендерингах.

Производительность

При работе с большим количеством объектов важны следующие аспекты:

  • использование useMemo для координатных массивов
  • группировка маркеров через LayerGroup
  • отключение лишних перерендеров компонентов
  • избегание пересоздания TileLayer

Пример оптимизации:

const positions = useMemo(() => generatePoints(), []);

Интеграция с внешними источниками данных

Карты часто работают с API:

  • динамическая подгрузка координат
  • потоковые обновления (WebSocket)
  • фильтрация объектов на лету
useEffect(() => {
  fetch("/api/points")
    .then(res => res.json())
    .then(setPoints);
}, []);

Контекст карты и вложенные компоненты

React-leaflet использует Context API React для передачи экземпляра карты вниз по дереву компонентов.

Любой дочерний компонент внутри MapContainer получает доступ к карте без явной передачи props.

Пользовательские оверлеи

Можно интегрировать кастомные DOM-элементы через DivOverlay или собственные компоненты.

<Marker position={[51.505, -0.09]}>
  <div className="custom-popup">
    Кастомный контент
  </div>
</Marker>

Работа с несколькими картами

Несколько MapContainer могут существовать независимо:

  • каждый создаёт отдельный Leaflet-инстанс
  • состояния не пересекаются
  • события изолированы

Это важно для dashboard-интерфейсов и аналитических панелей.

Расширение функциональности

React Leaflet допускает интеграцию с плагинами Leaflet:

  • heatmap-слои
  • clustering маркеров
  • кастомные контролы
  • анимации маршрутов

Подключение осуществляется через useEffect и доступ к map:

const map = useMap();

useEffect(() => {
  const control = L.control.scale();
  control.addTo(map);

  return () => control.remove();
}, [map]);