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

React中使用leaflet.heat报错TypeError: t.addLayer is not a function求助

React中Leaflet热力图无法正常显示的问题

问题现象

  • API能正常返回数据,但仅显示GeoJSON标记点,无热力图颜色
  • 复制解决方案后出现错误:TypeError: t.addLayer is not a function,错误关联getLocationCity函数
  • 调整L.heatLayer().addTo()位置后出现两种异常:
    • 放在setData()上方:地图无点位显示
    • 放在setData()下方:点位显示但无热力图颜色

相关代码片段

getLocationCity函数

export const getLocationCity = async (params) => {
  try {
    const body = DataCustom(params);
    const result = await instanceArcgis.post(
      "/rest/HomeLocation/11/query",
      body,
    );
    return await result?.data;
  }catch (error) {
    console.log(error);
  }
}

currentPosition Recoil原子

export const currentPosition = atom({
  key: "currentPosition",
  default: [-2.600, -11.01],
});

完整组件代码

import React, { useEffect, useState, useRef } from "react";
import { GeoJSON } from "react-leaflet";
import { useRecoilValue, useSetRecoilState } from "recoil";
import HeatmapOverlay from "leaflet-heatmap";
import { arcgisToken } from "../recoil";
import { mapBounds, loadingMap } from "../state";
import { getLocationCity } from "../data/arcgis";
import "leaflet.heat";

export default function HGood() {
    const [data, setData] = useState();
    const setIsLoading = useSetRecoilState(loadingMap);
    const bounds = useRecoilValue(mapBounds);
    const tokenArcgis = useRecoilValue(arcgisToken);
    const geoJsonLayer = useRef(null);

    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 = [];
                    const points = [];
                    response.features.forEach((element) => {
                        points.push([
                            element["geometry"]["x"],
                            element["geometry"]["y"],
                        ]);

                        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);
                    }

                    setData(FeatureCollection);
                    L.heatLayer(points).addTo(position);
                })
                .catch((err) => console.log(err))
                .finally(() => setIsLoading(false))
        };

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

    if (data) {
        return (
            <>
                <GeoJSON
                    ref={geoJsonLayer}
                    data={data}
                />
            </>
        );
    }
}

问题原因与修复方案

1. 核心错误分析

  • TypeError: t.addLayer is not a function:你调用L.heatLayer(points).addTo(position)时,position是Recoil存储的经纬度数组,不是Leaflet地图实例。addTo()方法必须传入地图实例,而非坐标值。
  • 热力图无颜色/不显示:一是没有正确挂载到地图实例;二是ArcGIS返回的坐标顺序是[经度, 纬度],但Leaflet的heatLayer要求[纬度, 经度],顺序错误导致热力图无法渲染;三是直接混用原生Leaflet方法和React-Leaflet组件,违背React组件化生命周期规范。

2. 修复步骤

步骤1:获取Leaflet地图实例

通过React-Leaflet的useMap钩子获取地图实例:

import { GeoJSON, useMap } from "react-leaflet"; // 新增useMap导入

在组件内添加:

const map = useMap(); // 获取地图实例

步骤2:封装React-Leaflet热力图组件(符合React规范)

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

const HeatLayer = createLayerComponent(
  (props, context) => {
    const { positions, ...rest } = props;
    const layer = L.heatLayer(positions, rest);
    return { layer, context };
  },
  (instance, props) => {
    if (props.positions) {
      instance.setLatLngs(props.positions);
    }
  }
);

步骤3:调整数据处理与状态管理

  • 新增状态存储热力图点位:
const [heatPoints, setHeatPoints] = useState([]);
  • 处理API返回数据时,修正坐标顺序:
response.features.forEach((element) => {
    // 反转坐标顺序:ArcGIS返回[经度,纬度],Leaflet需要[纬度,经度]
    points.push([
        element.geometry.y,
        element.geometry.x
    ]);
    // ...其他GeoJSON数据处理
});
setHeatPoints(points);

步骤4:移除错误的GeoDOM操作

React-Leaflet的GeoJSON组件会自动根据data状态更新,无需手动调用clearLayers()和addData(),删除以下代码:

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

修复后的完整组件代码

import React, { useEffect, useState } 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 { positions, ...rest } = props;
    const layer = L.heatLayer(positions, rest);
    return { layer, context };
  },
  (instance, props) => {
    if (props.positions) {
      instance.setLatLngs(props.positions);
    }
  }
);

export default function HGood() {
    const [data, setData] = useState();
    const [heatPoints, setHeatPoints] = useState([]);
    const setIsLoading = useSetRecoilState(loadingMap);
    const bounds = useRecoilValue(mapBounds);
    const tokenArcgis = useRecoilValue(arcgisToken);
    const map = useMap();

    useEffect(() => {
        const fetchDataSpeedtestKel = async () => {
            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);
            try {
                const response = await getLocationCity(body);
                const array = [];
                const points = [];
                response.features.forEach((element) => {
                    points.push([element.geometry.y, element.geometry.x]);
                    array.push({
                        type: "Feature",
                        properties: element.attributes,
                        geometry: {
                            type: "Point",
                            coordinates: [element.geometry.x, element.geometry.y],
                        },
                    });
                });

                setData({ type: "FeatureCollection", features: array });
                setHeatPoints(points);
            } catch (err) {
                console.log(err);
            } finally {
                setIsLoading(false);
            }
        };

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

    return (
        <>
            {data && <GeoJSON data={data} />}
            {heatPoints.length > 0 && <HeatLayer positions={heatPoints} />}
        </>
    );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 00:36:24