React中使用leaflet.heat遇TypeError: map.addLayer is not a function错误求助
问题分析与解决方案
核心错误原因
- 错误的图层添加目标:你调用
L.heatLayer(points).addTo(position)时,position是Recoil存储的坐标数组([-2.600, -11.01]),并非Leaflet Map实例。addTo()方法需要传入有效的Map对象,因此触发TypeError: map.addLayer is not a function。 - 未正确获取React-Leaflet Map实例:React-Leaflet封装了Leaflet的Map对象,直接使用全局
L对象操作时,必须先获取组件内的Map实例。 - 无效的状态更新:
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
相关产品推荐
相关产品推荐

