使用React useEffect切换React Leaflet tileLayer适配明暗主题报错求解
React Leaflet 3.2.0 主题切换瓦片层方案
错误原因
- 你将
L.tileLayer实例定义在组件根作用域,组件每次重渲染都会生成全新的图层实例,调用removeFrom时操作的不是已经挂载到地图上的实例,触发图层未挂载的报错 - 原useEffect未判断
map实例是否初始化完成,首次触发时map为null,执行图层操作直接报错 - JSX中默认声明的
TileLayer组件和你手动创建的亮色图层是重复实例,会出现图层叠加冲突
修复后完整代码
import React, { useState, useEffect, useContext, useRef } from "react"; import { Grid } from "@material-ui/core"; import { DarkContext } from '../../../Context/DarkContext'; import L from 'leaflet' import { MapContainer, Marker, Popup } from "react-leaflet"; import "leaflet/dist/leaflet.css"; import icon from "../constants"; const zoom = 13; function Map({ regionCoord, regionName }) { const [map, setMap] = useState(null); const { isDarkModeCont } = useContext(DarkContext); // 用useRef保存图层实例,避免重渲染重置 const darkLayerRef = useRef(L.tileLayer("https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png")) const lightLayerRef = useRef(L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png")) function turnLightMap() { if (!map) return darkLayerRef.current.removeFrom(map); lightLayerRef.current.addTo(map); } function turnDarkMap() { if (!map) return lightLayerRef.current.removeFrom(map); darkLayerRef.current.addTo(map); } // 统一处理初始化和主题变更的图层切换 useEffect(() => { if (!map) return // 先移除所有已挂载的图层 if (darkLayerRef.current.added) darkLayerRef.current.removeFrom(map) if (lightLayerRef.current.added) lightLayerRef.current.removeFrom(map) // 挂载对应主题的图层 isDarkModeCont ? turnDarkMap() : turnLightMap() // 组件卸载清理图层 return () => { darkLayerRef.current.remove() lightLayerRef.current.remove() } }, [isDarkModeCont, map]) return ( <> <Grid container> <Grid item xs={10}> {regionCoord && ( <MapContainer center={[50, 50]} zoom={zoom} style={{ height: "90vh" }} whenCreated={setMap} > {/* 去掉默认声明的TileLayer,改为手动管理图层 */} <Marker position={regionCoord} icon={icon}> <Popup>{regionName}</Popup> </Marker> </MapContainer> )} </Grid> <Grid item xs={2}> <button onClick={turnLightMap}>Light Map</button> <button onClick={turnDarkMap}>Dark Map</button> </Grid> </Grid> </> ); } export default Map;
关键修改说明
- 用
useRef存储两个瓦片图层实例,保证组件重渲染时操作的始终是同一个实例 - 给切换逻辑增加
map实例非空判断,避免操作未初始化的地图实例报错 - 移除JSX中默认声明的
TileLayer组件,所有图层操作统一通过手动创建的实例管理,避免实例冲突 - 合并初始化逻辑和主题切换逻辑到同一个
useEffect,依赖数组加入map和isDarkModeCont,保证状态变更时自动触发切换
内容的提问来源于stack exchange,提问作者Adriano
相关产品推荐
相关产品推荐

