From bf4fc95b8e5e2771dc9288c1b9ac6bf2558e74fa Mon Sep 17 00:00:00 2001 From: ISA Date: Tue, 19 Aug 2025 15:56:24 +0200 Subject: [PATCH] Fix Leaflet map initialization: prevent DOM errors and ensure robust container checks - Refactored initializeMap to accept the DOM node instead of the ref object - Updated all checks to use the DOM node directly - Improved useInitializeMap to only call initializeMap when the container is ready - Prevents "mapRef.current ist nicht definiert oder nicht im DOM" errors - Ensures map is only initialized when the container is attached --- .env.development | 2 +- .env.production | 2 +- components/mainComponent/MapComponent.js | 13 +++- .../mainComponent/hooks/useInitializeMap.js | 54 +++++++++++++- .../MapLayersControlPanel.js | 2 +- package-lock.json | 4 +- package.json | 2 +- utils/initializeMap.js | 74 ++++++++++--------- 8 files changed, 106 insertions(+), 47 deletions(-) diff --git a/.env.development b/.env.development index e8d921dcd..a79e5e979 100644 --- a/.env.development +++ b/.env.development @@ -25,4 +25,4 @@ NEXT_PUBLIC_USE_MOCKS=true NEXT_PUBLIC_BASE_PATH=/talas5 # Oder leer lassen für direkten Zugriff -> NEXT_PUBLIC_BASE_PATH= # App-Versionsnummer -NEXT_PUBLIC_APP_VERSION=1.1.316 +NEXT_PUBLIC_APP_VERSION=1.1.317 diff --git a/.env.production b/.env.production index 57884b5df..4508d33ee 100644 --- a/.env.production +++ b/.env.production @@ -26,4 +26,4 @@ NEXT_PUBLIC_BASE_PATH=/talas5 # Oder leer lassen für direkten Zugriff -> NEXT_PUBLIC_BASE_PATH= # App-Versionsnummer -NEXT_PUBLIC_APP_VERSION=1.1.316 +NEXT_PUBLIC_APP_VERSION=1.1.317 diff --git a/components/mainComponent/MapComponent.js b/components/mainComponent/MapComponent.js index b82e7b4a0..952218cd3 100644 --- a/components/mainComponent/MapComponent.js +++ b/components/mainComponent/MapComponent.js @@ -206,6 +206,16 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => { } }; //-----------------------------Map Initialisierung---------------- + // Default map options for Leaflet + const mapOptions = { + center: currentCenter, + zoom: currentZoom, + zoomControl: true, + contextmenu: true, + contextmenuWidth: 180, + contextmenuItems: [], + }; + useInitializeMap( map, mapRef, @@ -214,7 +224,8 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => { setMenuItemAdded, addItemsToMapContextMenu, hasRights, - value => dispatch(setDisabled(value)) + value => dispatch(setDisabled(value)), + mapOptions // pass mapOptions ); //-------------------------React Hooks-------------------------------- diff --git a/components/mainComponent/hooks/useInitializeMap.js b/components/mainComponent/hooks/useInitializeMap.js index 21114e3d2..a7abc38fd 100644 --- a/components/mainComponent/hooks/useInitializeMap.js +++ b/components/mainComponent/hooks/useInitializeMap.js @@ -2,12 +2,58 @@ import { useEffect } from "react"; import { initializeMap } from "../../../utils/initializeMap"; -const useInitializeMap = (map, mapRef, setMap, setOms, setMenuItemAdded, addItemsToMapContextMenu, hasRights, setPolylineEventsDisabled) => { +const useInitializeMap = ( + map, + mapRef, + setMap, + setOms, + setMenuItemAdded, + addItemsToMapContextMenu, + hasRights, + setPolylineEventsDisabled, + mapOptions +) => { useEffect(() => { - if (mapRef.current && !map) { - initializeMap(mapRef, setMap, setOms, setMenuItemAdded, addItemsToMapContextMenu, hasRights, setPolylineEventsDisabled); + let cancelled = false; + function tryInit(firstAttempt = true) { + if (cancelled) return; + // Only try to initialize if mapRef.current is ready and in DOM + if ( + mapRef.current && + mapRef.current instanceof HTMLElement && + document.body.contains(mapRef.current) && + !map && + !mapRef.current._leaflet_id + ) { + try { + const result = initializeMap( + mapRef.current, // pass DOM node, not ref + setMenuItemAdded, + addItemsToMapContextMenu, + hasRights, + setPolylineEventsDisabled, + firstAttempt // log error only on first real attempt + ); + if (result && result.map && result.oms) { + setMap(result.map); + setOms(result.oms); + } + } catch (error) { + if (process.env.NODE_ENV === "development") { + // eslint-disable-next-line no-console + console.warn("Map initialization error:", error); + } + } + } else if (!map && !cancelled) { + // If not ready, just retry after a short delay, do not call initializeMap at all + setTimeout(() => tryInit(false), 50); + } } - }, [mapRef, map, hasRights, setPolylineEventsDisabled]); + tryInit(true); + return () => { + cancelled = true; + }; + }, [mapRef, map, hasRights, setPolylineEventsDisabled, mapOptions]); }; export default useInitializeMap; diff --git a/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js b/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js index f10b87907..c1fdbc87d 100644 --- a/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js +++ b/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js @@ -343,7 +343,7 @@ function MapLayersControlPanel() { ).values(), */} {GisStationsStaticDistrict.Points.filter(p => !!p.Area_Name).map((item, index) => ( - ))} diff --git a/package-lock.json b/package-lock.json index d2ea20ead..c73893d61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "nodemap", - "version": "1.1.316", + "version": "1.1.317", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "nodemap", - "version": "1.1.316", + "version": "1.1.317", "dependencies": { "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", diff --git a/package.json b/package.json index ec65e2118..9e4f15e9e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nodemap", - "version": "1.1.316", + "version": "1.1.317", "dependencies": { "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", diff --git a/utils/initializeMap.js b/utils/initializeMap.js index 3c13584a4..088727c16 100644 --- a/utils/initializeMap.js +++ b/utils/initializeMap.js @@ -5,48 +5,50 @@ import "leaflet/dist/leaflet.css"; import "leaflet-contextmenu/dist/leaflet.contextmenu.css"; import "overlapping-marker-spiderfier-leaflet"; -export const initializeMap = async ( - mapRef, - setMap, - setOms, +export const initializeMap = ( + mapContainer, setMenuItemAdded, addItemsToMapContextMenu, hasRights, - setPolylineEventsDisabled + setPolylineEventsDisabled, + logError = false ) => { const basePath = process.env.NEXT_PUBLIC_BASE_PATH; - if (!mapRef.current) { - console.error("❌ Fehler: mapRef.current ist nicht definiert."); - return; + if ( + !mapContainer || + !(mapContainer instanceof HTMLElement) || + !document.body.contains(mapContainer) + ) { + if (logError) { + console.error("❌ Fehler: map container ist nicht definiert oder nicht im DOM."); + } + return null; } // Robuste Entfernung einer evtl. alten Leaflet-Instanz und Reset des DOM-Elements - if (mapRef.current) { - if (mapRef.current._leaflet_id) { + if (mapContainer) { + if (mapContainer._leaflet_id) { try { // Leaflet-Instanz entfernen - if ( - mapRef.current._leaflet_map && - typeof mapRef.current._leaflet_map.remove === "function" - ) { - mapRef.current._leaflet_map.remove(); + if (mapContainer._leaflet_map && typeof mapContainer._leaflet_map.remove === "function") { + mapContainer._leaflet_map.remove(); } // Leaflet 1.7+ speichert die Map-Instanz in L.Map._instances - if (L && L.Map && L.Map._instances && mapRef.current._leaflet_id) { - delete L.Map._instances[mapRef.current._leaflet_id]; + if (L && L.Map && L.Map._instances && mapContainer._leaflet_id) { + delete L.Map._instances[mapContainer._leaflet_id]; } // Auch in L.DomUtil._store ggf. entfernen - if (L && L.DomUtil && L.DomUtil._store && mapRef.current._leaflet_id) { - delete L.DomUtil._store[mapRef.current._leaflet_id]; + if (L && L.DomUtil && L.DomUtil._store && mapContainer._leaflet_id) { + delete L.DomUtil._store[mapContainer._leaflet_id]; } // _leaflet_id vom DOM-Element entfernen - delete mapRef.current._leaflet_id; + delete mapContainer._leaflet_id; // Alle weiteren _leaflet-Properties entfernen - for (const key in mapRef.current) { + for (const key in mapContainer) { if (key.startsWith("_leaflet")) { try { - delete mapRef.current[key]; + delete mapContainer[key]; } catch (e) {} } } @@ -55,22 +57,24 @@ export const initializeMap = async ( } } // Container leeren (immer, auch wenn keine Map-Instanz) - mapRef.current.innerHTML = ""; + mapContainer.innerHTML = ""; } - const url = new URL(window.location.origin); - let tileLayerUrl = ""; - + let tileLayerUrl = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"; try { if (window && window.__tileSource) { tileLayerUrl = window.__tileSource; } else { - const res = await fetch("/config.json"); - const config = await res.json(); - if (config.tileSources && config.active && config.tileSources[config.active]) { - window.__tileSource = config.tileSources[config.active]; - tileLayerUrl = window.__tileSource; - // Optional: Map reload oder Layer neu setzen, falls gewünscht + // Synchronous config fetch using XMLHttpRequest (since fetch is async) + const xhr = new XMLHttpRequest(); + xhr.open("GET", "/config.json", false); // false = synchronous + xhr.send(null); + if (xhr.status === 200) { + const config = JSON.parse(xhr.responseText); + if (config.tileSources && config.active && config.tileSources[config.active]) { + window.__tileSource = config.tileSources[config.active]; + tileLayerUrl = window.__tileSource; + } } } } catch (e) { @@ -79,7 +83,7 @@ export const initializeMap = async ( let initMap; try { - initMap = L.map(mapRef.current, { + initMap = L.map(mapContainer, { center: [53.111111, 8.4625], zoom: 12, minZoom: 5, @@ -114,10 +118,8 @@ export const initializeMap = async ( nearbyDistance: 20, }); - setMap(initMap); - setOms(overlappingMarkerSpiderfier); - if (typeof addItemsToMapContextMenu === "function") { addItemsToMapContextMenu(initMap, setMenuItemAdded, setPolylineEventsDisabled); } + return { map: initMap, oms: overlappingMarkerSpiderfier }; };