From cabd0d61c6a2e61477bd88168bf609f81f193543 Mon Sep 17 00:00:00 2001 From: Steven Date: Wed, 19 Nov 2025 22:21:01 +0800 Subject: [PATCH] fix(web): resolve Leaflet DOM cleanup error causing app crashes - Add MapCleanup component to properly remove Leaflet map instances on unmount - Fix LocationMarker initialization with useRef to prevent re-initialization - Remove problematic key prop in LocationDialog that caused unnecessary remounts - Fix goimports formatting in tag parser Fixes #5260 --- web/src/components/LeafletMap.tsx | 36 ++++++++++++++++--- .../InsertMenu/LocationDialog.tsx | 2 +- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/web/src/components/LeafletMap.tsx b/web/src/components/LeafletMap.tsx index f71122ce4..6cffccee6 100644 --- a/web/src/components/LeafletMap.tsx +++ b/web/src/components/LeafletMap.tsx @@ -1,8 +1,8 @@ import { DivIcon, LatLng } from "leaflet"; import { MapPinIcon } from "lucide-react"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import ReactDOMServer from "react-dom/server"; -import { MapContainer, Marker, TileLayer, useMapEvents } from "react-leaflet"; +import { MapContainer, Marker, TileLayer, useMap, useMapEvents } from "react-leaflet"; const markerIcon = new DivIcon({ className: "relative border-none", @@ -17,6 +17,7 @@ interface MarkerProps { const LocationMarker = (props: MarkerProps) => { const [position, setPosition] = useState(props.position); + const initializedRef = useRef(false); const map = useMapEvents({ click(e) { @@ -33,9 +34,12 @@ const LocationMarker = (props: MarkerProps) => { }); useEffect(() => { - map.attributionControl.setPrefix(""); - map.locate(); - }, []); + if (!initializedRef.current) { + map.attributionControl.setPrefix(""); + map.locate(); + initializedRef.current = true; + } + }, [map]); // Keep marker and map in sync with external position updates useEffect(() => { @@ -50,6 +54,27 @@ const LocationMarker = (props: MarkerProps) => { return position === undefined ? null : ; }; +const MapCleanup = () => { + const map = useMap(); + + useEffect(() => { + return () => { + // Cleanup map instance when component unmounts + setTimeout(() => { + if (map) { + try { + map.remove(); + } catch { + // Ignore errors during cleanup + } + } + }, 0); + }; + }, [map]); + + return null; +}; + interface MapProps { readonly?: boolean; latlng?: LatLng; @@ -64,6 +89,7 @@ const LeafletMap = (props: MapProps) => { {}} /> + ); }; diff --git a/web/src/components/MemoEditor/ActionButton/InsertMenu/LocationDialog.tsx b/web/src/components/MemoEditor/ActionButton/InsertMenu/LocationDialog.tsx index 52c367106..6a33c3913 100644 --- a/web/src/components/MemoEditor/ActionButton/InsertMenu/LocationDialog.tsx +++ b/web/src/components/MemoEditor/ActionButton/InsertMenu/LocationDialog.tsx @@ -51,7 +51,7 @@ export const LocationDialog = ({
- +