You need to enable JavaScript to run this app. 使用React useEffect切换React Leaflet tileLayer适配明暗主题报错求解
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.19 05:06:09