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

React中使用leaflet.heat遇TypeError: map.addLayer is not a function错误求助

问题分析与解决方案

核心错误原因

  1. 错误的图层添加目标:你调用L.heatLayer(points).addTo(position)时,position是Recoil存储的坐标数组([-2.600, -11.01]),并非Leaflet Map实例。addTo()方法需要传入有效的Map对象,因此触发TypeError: map.addLayer is not a function。
  2. 未正确获取React-Leaflet Map实例:React-Leaflet封装了Leaflet的Map对象,直接使用全局L对象操作时,必须先获取组件内的Map实例。
  3. 无效的状态更新:setPosition(data)中,data是FeatureCollection格式的数据,与currentPosition存储的坐标数组类型不匹配,属于逻辑错误。

修正步骤

步骤1:导入React-Leaflet的Map实例钩子

在组件顶部添加useMap钩子导入:

import { GeoJSON, useMap } from "react-leaflet";

步骤2:封装热力图组件(符合React-Leaflet组件化设计)

为了避免直接操作DOM实例,我们封装一个自定义HeatLayer组件:

import { createLayerComponent } from '@react-leaflet/core';
import L from 'leaflet';
import 'leaflet.heat';

// 封装Leaflet HeatLayer为React组件
const HeatLayer = createLayerComponent(
  (props, context) => {
    const layer = L.heatLayer(props.points, props.options);
    return { layer, context };
  },
  (instance, props, prevProps) => {
    // 数据更新时同步热力图坐标点
    if (props.points !== prevProps.points) {
      instance.setLatLngs(props.points);
    }
    // 配置更新时同步图层参数
    if (props.options !== prevProps.options) {
      instance.setOptions(props.options);
    }
  }
);

步骤3:修改HSales组件逻辑

  • 获取Map实例
  • 移除错误的addTo(position)和setPosition(data)代码
  • 新增热力图坐标点状态,渲染自定义HeatLayer组件

修正后的完整HSales组件代码:

import React, { useEffect, useState, useRef } from "react";
import { GeoJSON, useMap } from "react-leaflet";
import { useRecoilValue, useSetRecoilState } from "recoil";
import { createLayerComponent } from '@react-leaflet/core';
import L from 'leaflet';
import { arcgisToken } from "../recoil";
import { mapBounds, loadingMap } from "../state";
import { getLocationCity } from "../data/arcgis";
import "leaflet.heat";

// 封装热力图组件
const HeatLayer = createLayerComponent(
  (props, context) => {
    const layer = L.heatLayer(props.points, props.options);
    return { layer, context };
  },
  (instance, props, prevProps) => {
    if (props.points !== prevProps.points) {
      instance.setLatLngs(props.points);
    }
    if (props.options !== prevProps.options) {
      instance.setOptions(props.options);
    }
  }
);

export default function HSales() {
    const [data, setData] = useState();
    const [heatPoints, setHeatPoints] = useState([]); // 存储热力图坐标点
    const setIsLoading = useSetRecoilState(loadingMap);
    const bounds = useRecoilValue(mapBounds);
    const tokenArcgis = useRecoilValue(arcgisToken);
    const geoJsonLayer = useRef(null);
    const map = useMap(); // 获取Leaflet Map实例

    useEffect(() => {
        const fetchDataSpeedtestKel = () => {
            const body = {
                returnGeometry: true,
                rollbackOnFailure: true,
                geometry: JSON.stringify({
                    xmin: bounds.west,
                    ymin: bounds.south,
                    xmax: bounds.east,
                    ymax: bounds.north,
                    spatialReference: { wkid: 4326 },
                }),
                geometryType: "esriGeometryEnvelope",
                token: tokenArcgis,
            };

            setIsLoading(true);
            getLocationCity(body)
                .then((response) => {
                    const array = [];
                    response.features.forEach((element) => {
                        array.push({
                            type: "Feature",
                            properties: element["attributes"],
                            geometry: {
                                type: "Point",
                                coordinates: [
                                    element["geometry"]["x"],
                                    element["geometry"]["y"],
                                ],
                            },
                        });
                    });

                    const FeatureCollection = {
                        type: "FeatureCollection",
                        features: array,
                    };

                    if (geoJsonLayer.current) {
                        geoJsonLayer.current.clearLayers().addData(FeatureCollection);
                    }

                    // 注意:Leaflet坐标顺序是[纬度, 经度],需调换ArcGIS返回的x/y顺序
                    const points = response.features
                        ? response.features.map((element) => [
                            element["geometry"]["y"],
                            element["geometry"]["x"],
                            // 可选:添加权重值,如element.attributes.count,影响热力强度
                        ])
                        : [];

                    setData(FeatureCollection);
                    setHeatPoints(points);
                })
                .catch((err) => console.log(err))
                .finally(() => setIsLoading(false));
        };

        fetchDataSpeedtestKel();
    }, [bounds, setIsLoading, tokenArcgis, map]);

    return (
        <>
            {data && <GeoJSON ref={geoJsonLayer} data={data} />}
            {heatPoints.length > 0 && <HeatLayer points={heatPoints} options={{ radius: 25 }} />}
        </>
    );
}

额外注意事项

  • Leaflet坐标顺序为**[纬度, 经度]**,而ArcGIS返回的是[x(经度), y(纬度)],必须调换顺序否则热力图会显示到错误位置。
  • 可通过HeatLayer的options参数调整热力图半径、透明度、最大强度等,比如radius: 25控制热力点大小。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 08:54:21