To address the issue with onClick
event not working and have interactive React components as custom marker icons in react-leaflet
, here's a step-by-step solution:
- Utilize
L.divIcon
: Create a placeholderdiv
as the icon, which initially contains nothing. Assign a unique ID to thisdiv
for each marker. - Leverage Marker's
add
Event: Use theMarker
component'sadd
event to detect when the icon has been rendered into the DOM. - React Portal Integration: Once the icon is rendered, employ a React portal to render the actual React component within the placeholder
div
. Target thediv
using the unique ID. - Handle Marker Removal: Additionally, detect when the marker is removed and subsequently remove the portal.
To simplify this process, we can encapsulate this behavior in an EnhancedMarker
component:
import React, { useState, useId, useMemo } from "react"; import { createPortal } from "react-dom"; import { MapContainer, TileLayer, Marker } from "react-leaflet"; const EnhancedMarker = ({ eventHandlers, icon: providedIcon, ...otherProps }) => { const [markerRendered, setMarkerRendered] = useState(false); const id = "marker-" + useId(); const icon = useMemo( () => L.divIcon({ html: ``, }), [id] ); return ( <>{ setMarkerRendered(true); if (eventHandlers?.add) eventHandlers.add(...args); }, remove: (...args) => { setMarkerRendered(false); if (eventHandlers?.remove) eventHandlers.remove(...args); }, }} icon={icon} /> {markerRendered && createPortal(providedIcon, document.getElementById(id))} > ); }; const MarkerIconExample = () => { return ( <> > ); }; const CENTER = [51.505, -0.091]; const ZOOM = 13; const App = () => { return ( ); }; } />
For your specific use case, you can:
- Copy and incorporate the
EnhancedMarker
component into your code. - Replace instances of
<Marker>
with<EnhancedMarker>
. - Utilize
<IconGen />
within theicon
prop of<EnhancedMarker>
.