You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Leaflet地图添加标记集群与县边界报错及功能失效求助

Leaflet地图问题排查与修复

问题描述

  • 标记集群添加失败,报错:/~/components/MapBoundaries.tsx (138:34) map is not defined
  • 按缩放级别加载加州县边界功能失效,缩放相关console.log无输出

错误1:map is not defined 修复

问题根源

在JSX中直接调用markerClusterGroup.addTo(map),但map变量未定义;同时组件顶部声明的markerClusterGroup与useEffect内的同名变量存在作用域冲突,导致标记无法正确添加到集群。

修复步骤

  1. 删除JSX中的{markerClusterGroup.addTo(map)}代码,改用useEffect管理集群组的地图挂载
  2. 将组件顶部的markerClusterGroup改为用useRef存储,避免作用域覆盖:
    const markerClusterGroupRef = useRef(new MarkerCluster.MarkerClusterGroup());
    
  3. 在onEachFeature中通过ref实例添加标记:
    markerClusterGroupRef.current.addLayer(marker);
    
  4. 在useEffect中将集群组挂载到地图:
    if (mapRef.current && markerClusterGroupRef.current) {
      markerClusterGroupRef.current.addTo(mapRef.current);
    }
    

错误2:缩放事件无响应与县边界加载失效修复

问题根源

useEffect依赖数组为空,导致handleZoomChange读取的countyData始终是初始值;同时未清理地图事件监听,可能引发内存泄漏。

修复步骤

  1. 用useCallback包裹handleZoomChange,确保依赖更新时函数重新生成:
    const handleZoomChange = useCallback(() => {
      const currentZoom = mapRef.current?.getZoom();
      if (!currentZoom) return;
      
      if (currentZoom >= 8 && !countyData) {
        console.log('if currentZoom', currentZoom);
        setCountyData(CaliforniaCounty);
      } else if (currentZoom < 8 && countyData) {
        console.log('else if currentZoom', currentZoom);
        setCountyData(null);
      }
    }, [countyData]);
    
  2. 更新useEffect依赖数组,添加handleZoomChange,并添加清理函数移除事件监听:
    useEffect(() => {
      setGeoData(statesData);
      const map = mapRef.current;
      const clusterGroup = markerClusterGroupRef.current;
      
      if (map && clusterGroup) {
        clusterGroup.addTo(map);
        map.on('zoomend', handleZoomChange);
      }
      
      return () => {
        if (map) {
          map.off('zoomend', handleZoomChange);
        }
      };
    }, [handleZoomChange]);
    
  3. 直接导入本地JSON文件,避免fetch路径问题

完整修复后代码

import React, { useState, useEffect, useRef, useCallback } from 'react';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import CaliforniaCounty from './CaliforniaCounty.json';
import { MapContainer, TileLayer, GeoJSON } from 'react-leaflet';
import statesData from './US-ONLY-States.js';
import 'leaflet.markercluster/dist/MarkerCluster.css';
import 'leaflet.markercluster/dist/MarkerCluster.Default.css';
import * as MarkerCluster from 'leaflet.markercluster';

const MapBoundaries = () => {
  const [geoData, setGeoData] = useState(null);
  const markerClusterGroupRef = useRef(new MarkerCluster.MarkerClusterGroup());
  const [countyData, setCountyData] = useState(null);
  const mapRef = useRef();

  const handleZoomChange = useCallback(() => {
    const currentZoom = mapRef.current?.getZoom();
    if (!currentZoom) return;

    if (currentZoom >= 8 && !countyData) {
      console.log('if currentZoom', currentZoom);
      setCountyData(CaliforniaCounty);
    } else if (currentZoom < 8 && countyData) {
      console.log('else if currentZoom', currentZoom);
      setCountyData(null);
    }
  }, [countyData]);

  useEffect(() => {
    setGeoData(statesData);
    const map = mapRef.current;
    const clusterGroup = markerClusterGroupRef.current;

    if (map && clusterGroup) {
      clusterGroup.addTo(map);
      map.on('zoomend', handleZoomChange);
    }

    return () => {
      if (map) {
        map.off('zoomend', handleZoomChange);
      }
    };
  }, [handleZoomChange]);

  const getColor = (d) => {
    return d > 1000
      ? '#800026'
      : d > 500
      ? '#BD0026'
      : d > 200
      ? '#E31A1C'
      : d > 100
      ? '#FC4E2A'
      : d > 50
      ? '#FD8D3C'
      : d > 20
      ? '#FEB24C'
      : d > 10
      ? '#FED976'
      : '#FFEDA0';
  };

  const style = (feature) => {
    return {
      fillColor: getColor(feature.properties.dataValue),
      weight: 2,
      opacity: 1,
      color: 'white',
      dashArray: '3',
      fillOpacity: 0.7,
    };
  };

  const onEachFeature = (feature, layer) => {
    const marker = L.marker(layer.getBounds().getCenter());
    marker.bindPopup(`${feature.properties.name}: ${feature.properties.dataValue}`);
    layer.bindPopup(`${feature.properties.name}: ${feature.properties.dataValue}`);
    markerClusterGroupRef.current.addLayer(marker);
  };

  const onEachCountyFeature = (feature, layer) => {
    layer.bindPopup(`County Name: ${feature.properties.name}`);
  };

  return (
    <MapContainer
      center={[37.09024, -95.712891]}
      zoom={6}
      style={{ height: '500px' }}
      ref={mapRef}
    >
      <TileLayer
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
      />
      {geoData && (
        <GeoJSON data={geoData} style={style} onEachFeature={onEachFeature} />
      )}
      {countyData && (
        <GeoJSON
          data={countyData}
          style={{ color: 'black', weight: 1 }}
          onEachFeature={onEachCountyFeature}
        />
      )}
    </MapContainer>
  );
};

export default MapBoundaries;

内容的提问来源于stack exchange,提问作者Ram01

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 12:04:56