feat: Marker-Cleanup zur Vermeidung von Memory Leaks implementiert

- cleanupMarkers() Utility in /utils/common/cleanupMarkers.js erstellt
- Marker-Cleanup in MapComponent.js vor createAndSetDevices() integriert
- createAndSetDevices.js von Cleanup-Verantwortung befreit (reine Erzeugung)
- setupPOIs.js erweitert um cleanupMarkers() vor Layer-Neuerstellung
- poiUtils.js und markerUtils.js angepasst: cleanupMarkers() ersetzt .remove()
- Memory Leaks durch verwaiste Tooltips, Events und Marker behoben
- Grundlage für wiederverwendbare Marker-Cleanup-Logik für POIs, Geräte, Linien geschaffen
This commit is contained in:
ISA
2025-06-06 10:21:56 +02:00
parent ec31b36b3d
commit f7f7122620
10 changed files with 160 additions and 55 deletions

View File

@@ -77,6 +77,8 @@ import { updateAreaThunk } from "@/redux/thunks/database/area/updateAreaThunk";
import useDynamicDeviceLayers from "@/hooks/useDynamicDeviceLayers.js";
import useDataUpdater from "@/hooks/useDataUpdater";
import { cleanupPolylinesForMemory } from "@/utils/polylines/cleanupPolylinesForMemory";
import { cleanupMarkers } from "@/utils/common/cleanupMarkers";
//-----------------------------------------------------------------------------------------------------
const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
//-------------------------------
@@ -299,9 +301,13 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
useEffect(() => {
if (!map) return;
// Entferne alte Marker und Polylinien
markers.forEach(marker => marker.remove());
polylines.forEach(polyline => polyline.remove());
// vorherige Marker & Polylinien vollständig bereinigen
markers.forEach(marker => {
console.log("Marker-Typ:", marker.options);
marker.remove();
});
cleanupPolylinesForMemory(polylines, map);
// Setze neue Marker und Polylinien mit den aktuellen Daten
const { markers: newMarkers, polylines: newPolylines } = setupPolylines(
@@ -358,7 +364,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
polyline.closeTooltip();
});
});
cleanupMarkers(markers, oms);
setMarkers(newMarkers);
setPolylines(newPolylines);
}, [
@@ -471,14 +477,15 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
}
}, [map]);
//--------------------------------------------
let timeoutId;
useEffect(() => {
const initializeContextMenu = () => {
if (map) {
map.whenReady(() => {
setTimeout(() => {
timeoutId = setTimeout(() => {
if (map.contextmenu) {
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
//console.log("Contextmenu ist vorhanden");
console.log("Contextmenu ist vorhanden");
}
} else {
console.warn("Contextmenu ist nicht verfügbar.");
@@ -489,6 +496,10 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
};
initializeContextMenu();
return () => {
clearTimeout(timeoutId); // Aufräumen
};
}, [map]);
//---------------------------------------
@@ -693,34 +704,28 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
}
}, [isPolylineContextMenuOpen, countdown, countdownActive, dispatch, window.map]);
//----------------------------------
// map in window setzen für mehr Debugging
useEffect(() => {
if (map) {
window.map = map;
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
console.log("✅ window.map wurde gesetzt:", window.map);
}
}
}, [map]);
//---------------------------------------
// **Fehlerbehandlung für `contextmenu`**
// damit den Fehler mit contextmenu nicht angezeigt wird und überspringt wird und die Seite neu geladen wird
useEffect(() => {
let timeoutId;
window.onerror = function (message, source, lineno, colno, error) {
if (message.includes("Cannot read properties of null (reading 'contextmenu')")) {
console.warn("⚠️ Fehler mit `contextmenu` erkannt - Neuladen der Seite.");
setTimeout(() => {
console.warn("⚠️ Fehler mit `contextmenu` erkannt Neuladen der Seite.");
timeoutId = setTimeout(() => {
window.location.reload();
}, 0); // **Seite nach Sekunde neu laden**
return true; // **Fehler unterdrücken, damit React ihn nicht anzeigt**
}, 0);
return true; // Fehler unterdrücken
}
};
return () => {
window.onerror = null; // **Fehlerbehandlung entfernen, wenn Komponente unmounted wird**
clearTimeout(timeoutId);
window.onerror = null; // Aufräumen beim Unmount
};
}, []);
//------------------------------------------------
useEffect(() => {
if (poiTypStatus === "succeeded" && Array.isArray(poiTypData)) {
@@ -736,7 +741,6 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
}
}, [poiIconsData, poiIconsStatus]);
//-----------------------------------------------------------------
//-----------------------------------------------------------------
useEffect(() => {
if (!map) return;
@@ -761,16 +765,6 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
checkOverlappingMarkers(map, allMarkers, plusRoundIcon);
}, [map, markerStates, mapLayersVisibility]);
//---------------------------------------------
// 🧠 Optional für Debugging für überlappende Markers
useEffect(() => {
if (oms) {
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
console.log("📌 OMS ready:", oms);
}
window.oms = oms; // Für Debugging global
}
}, [oms]);
//----------------------------------------------
useEffect(() => {
@@ -780,6 +774,12 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
console.log("Production Mode aktiviert");
}
}, []);
//-------------------------------------------
useEffect(() => {
return () => {
cleanupMarkers(markers, oms);
};
}, []);
//---------------------------------------------
//--------------------------------------------
return (