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
相关产品推荐
相关产品推荐

