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
This commit is contained in:
@@ -25,4 +25,4 @@ NEXT_PUBLIC_USE_MOCKS=true
|
|||||||
NEXT_PUBLIC_BASE_PATH=/talas5
|
NEXT_PUBLIC_BASE_PATH=/talas5
|
||||||
# Oder leer lassen für direkten Zugriff -> NEXT_PUBLIC_BASE_PATH=
|
# Oder leer lassen für direkten Zugriff -> NEXT_PUBLIC_BASE_PATH=
|
||||||
# App-Versionsnummer
|
# App-Versionsnummer
|
||||||
NEXT_PUBLIC_APP_VERSION=1.1.316
|
NEXT_PUBLIC_APP_VERSION=1.1.317
|
||||||
|
|||||||
@@ -26,4 +26,4 @@ NEXT_PUBLIC_BASE_PATH=/talas5
|
|||||||
# Oder leer lassen für direkten Zugriff -> NEXT_PUBLIC_BASE_PATH=
|
# Oder leer lassen für direkten Zugriff -> NEXT_PUBLIC_BASE_PATH=
|
||||||
|
|
||||||
# App-Versionsnummer
|
# App-Versionsnummer
|
||||||
NEXT_PUBLIC_APP_VERSION=1.1.316
|
NEXT_PUBLIC_APP_VERSION=1.1.317
|
||||||
|
|||||||
@@ -206,6 +206,16 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
//-----------------------------Map Initialisierung----------------
|
//-----------------------------Map Initialisierung----------------
|
||||||
|
// Default map options for Leaflet
|
||||||
|
const mapOptions = {
|
||||||
|
center: currentCenter,
|
||||||
|
zoom: currentZoom,
|
||||||
|
zoomControl: true,
|
||||||
|
contextmenu: true,
|
||||||
|
contextmenuWidth: 180,
|
||||||
|
contextmenuItems: [],
|
||||||
|
};
|
||||||
|
|
||||||
useInitializeMap(
|
useInitializeMap(
|
||||||
map,
|
map,
|
||||||
mapRef,
|
mapRef,
|
||||||
@@ -214,7 +224,8 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
setMenuItemAdded,
|
setMenuItemAdded,
|
||||||
addItemsToMapContextMenu,
|
addItemsToMapContextMenu,
|
||||||
hasRights,
|
hasRights,
|
||||||
value => dispatch(setDisabled(value))
|
value => dispatch(setDisabled(value)),
|
||||||
|
mapOptions // pass mapOptions
|
||||||
);
|
);
|
||||||
|
|
||||||
//-------------------------React Hooks--------------------------------
|
//-------------------------React Hooks--------------------------------
|
||||||
|
|||||||
@@ -2,12 +2,58 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { initializeMap } from "../../../utils/initializeMap";
|
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(() => {
|
useEffect(() => {
|
||||||
if (mapRef.current && !map) {
|
let cancelled = false;
|
||||||
initializeMap(mapRef, setMap, setOms, setMenuItemAdded, addItemsToMapContextMenu, hasRights, setPolylineEventsDisabled);
|
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);
|
||||||
}
|
}
|
||||||
}, [mapRef, map, hasRights, setPolylineEventsDisabled]);
|
} 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tryInit(true);
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}, [mapRef, map, hasRights, setPolylineEventsDisabled, mapOptions]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useInitializeMap;
|
export default useInitializeMap;
|
||||||
|
|||||||
@@ -343,7 +343,7 @@ function MapLayersControlPanel() {
|
|||||||
).values(),
|
).values(),
|
||||||
*/}
|
*/}
|
||||||
{GisStationsStaticDistrict.Points.filter(p => !!p.Area_Name).map((item, index) => (
|
{GisStationsStaticDistrict.Points.filter(p => !!p.Area_Name).map((item, index) => (
|
||||||
<option key={item.Area_Name} value={item.IdLD}>
|
<option key={item.Area_Name + "-" + item.IdLD} value={item.IdLD}>
|
||||||
{item.Area_Name}
|
{item.Area_Name}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "nodemap",
|
"name": "nodemap",
|
||||||
"version": "1.1.316",
|
"version": "1.1.317",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "nodemap",
|
"name": "nodemap",
|
||||||
"version": "1.1.316",
|
"version": "1.1.317",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.13.3",
|
"@emotion/react": "^11.13.3",
|
||||||
"@emotion/styled": "^11.13.0",
|
"@emotion/styled": "^11.13.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "nodemap",
|
"name": "nodemap",
|
||||||
"version": "1.1.316",
|
"version": "1.1.317",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.13.3",
|
"@emotion/react": "^11.13.3",
|
||||||
"@emotion/styled": "^11.13.0",
|
"@emotion/styled": "^11.13.0",
|
||||||
|
|||||||
@@ -5,48 +5,50 @@ import "leaflet/dist/leaflet.css";
|
|||||||
import "leaflet-contextmenu/dist/leaflet.contextmenu.css";
|
import "leaflet-contextmenu/dist/leaflet.contextmenu.css";
|
||||||
import "overlapping-marker-spiderfier-leaflet";
|
import "overlapping-marker-spiderfier-leaflet";
|
||||||
|
|
||||||
export const initializeMap = async (
|
export const initializeMap = (
|
||||||
mapRef,
|
mapContainer,
|
||||||
setMap,
|
|
||||||
setOms,
|
|
||||||
setMenuItemAdded,
|
setMenuItemAdded,
|
||||||
addItemsToMapContextMenu,
|
addItemsToMapContextMenu,
|
||||||
hasRights,
|
hasRights,
|
||||||
setPolylineEventsDisabled
|
setPolylineEventsDisabled,
|
||||||
|
logError = false
|
||||||
) => {
|
) => {
|
||||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH;
|
const basePath = process.env.NEXT_PUBLIC_BASE_PATH;
|
||||||
|
|
||||||
if (!mapRef.current) {
|
if (
|
||||||
console.error("❌ Fehler: mapRef.current ist nicht definiert.");
|
!mapContainer ||
|
||||||
return;
|
!(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
|
// Robuste Entfernung einer evtl. alten Leaflet-Instanz und Reset des DOM-Elements
|
||||||
if (mapRef.current) {
|
if (mapContainer) {
|
||||||
if (mapRef.current._leaflet_id) {
|
if (mapContainer._leaflet_id) {
|
||||||
try {
|
try {
|
||||||
// Leaflet-Instanz entfernen
|
// Leaflet-Instanz entfernen
|
||||||
if (
|
if (mapContainer._leaflet_map && typeof mapContainer._leaflet_map.remove === "function") {
|
||||||
mapRef.current._leaflet_map &&
|
mapContainer._leaflet_map.remove();
|
||||||
typeof mapRef.current._leaflet_map.remove === "function"
|
|
||||||
) {
|
|
||||||
mapRef.current._leaflet_map.remove();
|
|
||||||
}
|
}
|
||||||
// Leaflet 1.7+ speichert die Map-Instanz in L.Map._instances
|
// Leaflet 1.7+ speichert die Map-Instanz in L.Map._instances
|
||||||
if (L && L.Map && L.Map._instances && mapRef.current._leaflet_id) {
|
if (L && L.Map && L.Map._instances && mapContainer._leaflet_id) {
|
||||||
delete L.Map._instances[mapRef.current._leaflet_id];
|
delete L.Map._instances[mapContainer._leaflet_id];
|
||||||
}
|
}
|
||||||
// Auch in L.DomUtil._store ggf. entfernen
|
// Auch in L.DomUtil._store ggf. entfernen
|
||||||
if (L && L.DomUtil && L.DomUtil._store && mapRef.current._leaflet_id) {
|
if (L && L.DomUtil && L.DomUtil._store && mapContainer._leaflet_id) {
|
||||||
delete L.DomUtil._store[mapRef.current._leaflet_id];
|
delete L.DomUtil._store[mapContainer._leaflet_id];
|
||||||
}
|
}
|
||||||
// _leaflet_id vom DOM-Element entfernen
|
// _leaflet_id vom DOM-Element entfernen
|
||||||
delete mapRef.current._leaflet_id;
|
delete mapContainer._leaflet_id;
|
||||||
// Alle weiteren _leaflet-Properties entfernen
|
// Alle weiteren _leaflet-Properties entfernen
|
||||||
for (const key in mapRef.current) {
|
for (const key in mapContainer) {
|
||||||
if (key.startsWith("_leaflet")) {
|
if (key.startsWith("_leaflet")) {
|
||||||
try {
|
try {
|
||||||
delete mapRef.current[key];
|
delete mapContainer[key];
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,22 +57,24 @@ export const initializeMap = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Container leeren (immer, auch wenn keine Map-Instanz)
|
// Container leeren (immer, auch wenn keine Map-Instanz)
|
||||||
mapRef.current.innerHTML = "";
|
mapContainer.innerHTML = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = new URL(window.location.origin);
|
let tileLayerUrl = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png";
|
||||||
let tileLayerUrl = "";
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (window && window.__tileSource) {
|
if (window && window.__tileSource) {
|
||||||
tileLayerUrl = window.__tileSource;
|
tileLayerUrl = window.__tileSource;
|
||||||
} else {
|
} else {
|
||||||
const res = await fetch("/config.json");
|
// Synchronous config fetch using XMLHttpRequest (since fetch is async)
|
||||||
const config = await res.json();
|
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]) {
|
if (config.tileSources && config.active && config.tileSources[config.active]) {
|
||||||
window.__tileSource = config.tileSources[config.active];
|
window.__tileSource = config.tileSources[config.active];
|
||||||
tileLayerUrl = window.__tileSource;
|
tileLayerUrl = window.__tileSource;
|
||||||
// Optional: Map reload oder Layer neu setzen, falls gewünscht
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -79,7 +83,7 @@ export const initializeMap = async (
|
|||||||
|
|
||||||
let initMap;
|
let initMap;
|
||||||
try {
|
try {
|
||||||
initMap = L.map(mapRef.current, {
|
initMap = L.map(mapContainer, {
|
||||||
center: [53.111111, 8.4625],
|
center: [53.111111, 8.4625],
|
||||||
zoom: 12,
|
zoom: 12,
|
||||||
minZoom: 5,
|
minZoom: 5,
|
||||||
@@ -114,10 +118,8 @@ export const initializeMap = async (
|
|||||||
nearbyDistance: 20,
|
nearbyDistance: 20,
|
||||||
});
|
});
|
||||||
|
|
||||||
setMap(initMap);
|
|
||||||
setOms(overlappingMarkerSpiderfier);
|
|
||||||
|
|
||||||
if (typeof addItemsToMapContextMenu === "function") {
|
if (typeof addItemsToMapContextMenu === "function") {
|
||||||
addItemsToMapContextMenu(initMap, setMenuItemAdded, setPolylineEventsDisabled);
|
addItemsToMapContextMenu(initMap, setMenuItemAdded, setPolylineEventsDisabled);
|
||||||
}
|
}
|
||||||
|
return { map: initMap, oms: overlappingMarkerSpiderfier };
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user