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
This commit is contained in:
Steven 2025-11-19 22:21:01 +08:00
parent 3989100a27
commit cabd0d61c6
2 changed files with 32 additions and 6 deletions

View file

@ -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 : <Marker position={position} icon={markerIcon}></Marker>;
};
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) => {
<MapContainer className="w-full h-72" center={position} zoom={13} scrollWheelZoom={false}>
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
<LocationMarker position={position} readonly={props.readonly} onChange={props.onChange ? props.onChange : () => {}} />
<MapCleanup />
</MapContainer>
);
};

View file

@ -51,7 +51,7 @@ export const LocationDialog = ({
</VisuallyHidden>
<div className="flex flex-col">
<div className="w-full h-64 overflow-hidden rounded-t-md bg-muted/30">
<LeafletMap key={JSON.stringify(locationInitialized)} latlng={position} onChange={onPositionChange} />
<LeafletMap latlng={position} onChange={onPositionChange} />
</div>
<div className="w-full flex flex-col p-3 gap-3">
<div className="grid grid-cols-2 gap-3">