fix: Behebt TypeError: Cannot read properties of null (reading 'contextmenu') mit Redux

- Implementiert `store.subscribe()` in `setupPolylines.js`, um das Kontextmenü-Handling über Redux zu steuern.
- Ersetzt `useDispatch()` und `useSelector()` durch `store.dispatch()` und `store.getState()` in einer Nicht-React-Datei.
- Fügt eine `forceClose`-Action in `polylineContextMenuSlice.js` hinzu, um das Kontextmenü synchron mit `setInterval` zu schließen.
- Stellt sicher, dass das Kontextmenü **immer vor Ablauf des 20-Sekunden-Intervalls** geschlossen wird.
- Verhindert doppelte Menüinstanzen und sorgt für ein stabiles Verhalten bei wiederholten Interaktionen.

 Fix für `TypeError: Cannot read properties of null (reading 'contextmenu')`
 **Verhindert Kontextmenü-Fehler beim automatischen Datenupdate**
 **Redux-gesteuerte Menüverwaltung für stabilere Performance**
 **Kein unerwartetes Offenbleiben oder erneutes Rendern des Menüs mehr**
This commit is contained in:
ISA
2025-03-10 13:49:11 +01:00
parent 1298ce3a81
commit 8ab1c53996
5 changed files with 110 additions and 25 deletions

View File

@@ -79,10 +79,15 @@ import ShowAddStationPopup from "../AddPOIModal.js";
import { useInitGisStationsStatic } from "../mainComponent/hooks/useInitGisStationsStatic"; import { useInitGisStationsStatic } from "../mainComponent/hooks/useInitGisStationsStatic";
import { closeAddPoiModal } from "../../redux/slices/addPoiOnPolylineSlice.js"; import { closeAddPoiModal } from "../../redux/slices/addPoiOnPolylineSlice.js";
import AddPOIOnPolyline from "../AddPOIOnPolyline"; import AddPOIOnPolyline from "../AddPOIOnPolyline";
import { closePolylineContextMenu } from "../../redux/slices/polylineContextMenuSlice";
import { forceCloseContextMenu } from "../../redux/slices/polylineContextMenuSlice";
const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => { const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const isPolylineContextMenuOpen = useSelector((state) => state.polylineContextMenu.isOpen);
const contextMenuState = useSelector((state) => state.polylineContextMenu);
const polylinePosition = contextMenuState.position ? L.latLng(contextMenuState.position.lat, contextMenuState.position.lng) : null;
const currentPoi = useSelector(selectCurrentPoi); const currentPoi = useSelector(selectCurrentPoi);
//const setCurrentPoi = useSetRecoilState(currentPoiState); //const setCurrentPoi = useSetRecoilState(currentPoiState);
const polylineVisible = useSelector(selectPolylineVisible); const polylineVisible = useSelector(selectPolylineVisible);
@@ -876,6 +881,13 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
console.log("🔥 Automatischer Klick-Event ausgelöst, um Spiderfy zu aktualisieren."); console.log("🔥 Automatischer Klick-Event ausgelöst, um Spiderfy zu aktualisieren.");
map.fire("click"); map.fire("click");
} }
if (isPolylineContextMenuOpen) {
dispatch(closePolylineContextMenu()); // Schließe das Kontextmenü, bevor das nächste Update passiert
}
if (map) {
console.log("🔥 nochmal klick.");
map.fire("click");
}
}, 20000); }, 20000);
// Aufräumen bei Komponentenentladung // Aufräumen bei Komponentenentladung

View File

@@ -1,2 +1,2 @@
// /config/appVersion // /config/appVersion
export const APP_VERSION = "1.1.41"; export const APP_VERSION = "1.1.42";

View File

@@ -0,0 +1,32 @@
// redux/slices/polylineContextMenuSlice.js
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
isOpen: false,
position: null, // Serialisierbarer Wert { lat, lng }
forceClose: false, // Neuer State, um das Schließen zu erzwingen
};
const polylineContextMenuSlice = createSlice({
name: "polylineContextMenu",
initialState,
reducers: {
openPolylineContextMenu: (state, action) => {
state.isOpen = true;
state.position = { lat: action.payload.position.lat, lng: action.payload.position.lng };
state.forceClose = false; // Beim Öffnen zurücksetzen
},
closePolylineContextMenu: (state) => {
state.isOpen = false;
state.position = null;
},
forceCloseContextMenu: (state) => {
state.isOpen = false;
state.position = null;
state.forceClose = true; // Setzt Flagge, um Schließen zu signalisieren
},
},
});
export const { openPolylineContextMenu, closePolylineContextMenu, forceCloseContextMenu } = polylineContextMenuSlice.actions;
export default polylineContextMenuSlice.reducer;

View File

@@ -11,6 +11,7 @@ import gisSystemStaticReducer from "./slices/webService/gisSystemStaticSlice";
import gisStationsStaticReducer from "./slices/webService/gisStationsStaticSlice"; import gisStationsStaticReducer from "./slices/webService/gisStationsStaticSlice";
import poiTypesReducer from "./slices/db/poiTypesSlice"; import poiTypesReducer from "./slices/db/poiTypesSlice";
import addPoiOnPolylineReducer from "./slices/addPoiOnPolylineSlice"; import addPoiOnPolylineReducer from "./slices/addPoiOnPolylineSlice";
import polylineContextMenuReducer from "./slices/polylineContextMenuSlice";
export const store = configureStore({ export const store = configureStore({
reducer: { reducer: {
@@ -25,5 +26,6 @@ export const store = configureStore({
gisStationsStatic: gisStationsStaticReducer, gisStationsStatic: gisStationsStaticReducer,
poiTypes: poiTypesReducer, poiTypes: poiTypesReducer,
addPoiOnPolyline: addPoiOnPolylineReducer, addPoiOnPolyline: addPoiOnPolylineReducer,
polylineContextMenu: polylineContextMenuReducer,
}, },
}); });

View File

@@ -15,6 +15,7 @@ import { polylineLayerVisibleState } from "../redux/slices/polylineLayerVisibleS
import { useRecoilValue } from "recoil"; import { useRecoilValue } from "recoil";
import { store } from "../redux/store"; // Importiere den Store import { store } from "../redux/store"; // Importiere den Store
import { openAddPoiOnPolylineModal } from "../redux/slices/addPoiOnPolylineSlice"; import { openAddPoiOnPolylineModal } from "../redux/slices/addPoiOnPolylineSlice";
import { openPolylineContextMenu, closePolylineContextMenu } from "../redux/slices/polylineContextMenuSlice";
// Funktion zum Deaktivieren der Polyline-Ereignisse // Funktion zum Deaktivieren der Polyline-Ereignisse
export function disablePolylineEvents(polylines) { export function disablePolylineEvents(polylines) {
@@ -51,39 +52,60 @@ export function enablePolylineEvents(polylines, lineColors) {
// Funktion zum Schließen des Kontextmenüs und Entfernen der Markierung // Funktion zum Schließen des Kontextmenüs und Entfernen der Markierung
function closePolylineSelectionAndContextMenu(map) { function closePolylineSelectionAndContextMenu(map) {
try { try {
// Entferne alle markierten Polylinien // Falls eine Polyline aktiv ist, entfernen
if (window.selectedPolyline) { if (window.selectedPolyline) {
window.selectedPolyline.setStyle({ weight: 3 }); // Originalstil wiederherstellen window.selectedPolyline.setStyle({ weight: 3 });
window.selectedPolyline = null; window.selectedPolyline = null;
} }
// Überprüfe, ob map und map.contextmenu definiert sind // Überprüfen, ob das Kontextmenü existiert, bevor es geschlossen wird
if (map && map.contextmenu) { if (map && map.contextmenu && typeof map.contextmenu.hide === "function") {
map.contextmenu.hide(); // Kontextmenü schließen map.contextmenu.hide(); // Menü schließen
} else { } else {
console.warn("Kontextmenü ist nicht verfügbar."); console.warn("Kontextmenü existiert nicht mehr oder wurde bereits entfernt.");
} }
} catch (error) { } catch (error) {
console.error("Fehler beim Schließen des Kontextmenüs:", error); console.error("Fehler beim Schließen des Kontextmenüs:", error);
window.location.reload();
} }
// Countdown-Status zurücksetzen // Lokale Speicherwerte zurücksetzen
localStorage.removeItem("contextMenuCountdown"); localStorage.removeItem("contextMenuCountdown");
localStorage.removeItem("contextMenuExpired"); localStorage.removeItem("contextMenuExpired");
} }
// Überprüft regelmäßig den Status in localStorage // Überprüft regelmäßig den Status in localStorage
function monitorContextMenu(map) { function monitorContextMenu(map) {
setInterval(() => { function checkAndClose() {
const isContextMenuExpired = localStorage.getItem("contextMenuExpired") === "true"; const isContextMenuExpired = localStorage.getItem("contextMenuExpired") === "true";
if (isContextMenuExpired) {
closePolylineSelectionAndContextMenu(map);
localStorage.removeItem("contextMenuExpired"); // Flagge entfernen, um wiederverwendbar zu sein
}
}, 1000); // Alle 1 Sekunde überprüfen
}
if (isContextMenuExpired) {
if (map && map.contextmenu && typeof map.contextmenu.hide === "function") {
closePolylineSelectionAndContextMenu(map);
localStorage.removeItem("contextMenuExpired");
} else {
console.warn("Kontextmenü war nicht verfügbar und konnte nicht geschlossen werden.");
}
}
setTimeout(checkAndClose, 1000); // **Recursive Timeout statt Intervall**
}
checkAndClose();
}
//------------------------------------------
store.subscribe(() => {
const state = store.getState(); // Redux-Toolkit empfohlene Methode
if (state.polylineContextMenu.forceClose) {
console.log("🚀 Redux-Event erkannt - Kontextmenü wird geschlossen.");
store.dispatch(closePolylineContextMenu());
if (window.map && window.map.contextmenu) {
window.map.contextmenu.hide();
}
}
});
//--------------------------------------------
export const setupPolylines = (map, linePositions, lineColors, tooltipContents, setNewCoords, tempMarker, currentZoom, currentCenter, polylineVisible) => { export const setupPolylines = (map, linePositions, lineColors, tooltipContents, setNewCoords, tempMarker, currentZoom, currentCenter, polylineVisible) => {
if (!polylineVisible) { if (!polylineVisible) {
console.warn("Polylines deaktiviert - keine Zeichnung"); console.warn("Polylines deaktiviert - keine Zeichnung");
@@ -292,7 +314,7 @@ export const setupPolylines = (map, linePositions, lineColors, tooltipContents,
store.dispatch(openAddPoiOnPolylineModal(e.latlng)); store.dispatch(openAddPoiOnPolylineModal(e.latlng));
}, },
}, },
*/ */
{ {
text: "Stützpunkt hinzufügen", text: "Stützpunkt hinzufügen",
icon: "/img/icons/gisLines/add-support-point.svg", icon: "/img/icons/gisLines/add-support-point.svg",
@@ -404,15 +426,32 @@ export const setupPolylines = (map, linePositions, lineColors, tooltipContents,
localStorage.setItem("polylineLink", link); localStorage.setItem("polylineLink", link);
}); });
*/ */
// Starte den Timer zum Schließen des Kontextmenüs nach 15 Sekunden
polyline.on("contextmenu", function (e) {
const contextMenu = this._map.contextmenu; // Zugriff auf das Kontextmenü
const closeMenu = () => contextMenu.hide(); // Funktion zum Schließen des Menüs
const countdown = parseInt(localStorage.getItem("contextMenuCountdown"), 30); // Event-Listener für Redux Store-Änderungen registrieren
if (countdown >= 28) {
closeMenu(); // Starte den Timer zum Schließen des Kontextmenüs nach 15 Sekunden
}
polyline.on("contextmenu", (e) => {
store.dispatch(closePolylineContextMenu());
setTimeout(() => {
if (!map || !map.contextmenu) return;
store.dispatch(
openPolylineContextMenu({
position: { lat: e.latlng.lat, lng: e.latlng.lng },
polylineId: polyline.options.idLD,
})
);
// Schließen nach 17 Sekunden
setTimeout(() => {
store.dispatch(closePolylineContextMenu());
if (map.contextmenu) {
map.contextmenu.hide();
}
}, 17000);
}, 50);
}); });
polylines.push(polyline); polylines.push(polyline);