i'm trying to bind react-bootstrap accordion to a leaflet.js marker. when clicking on accordion, marker will get zoomed-in... the problem is i have to "double click" on the accordion to collapse it and zoom-in the marker => because accordion is being refreshed when focusing on marker
**codesandbox demo **
https://codesandbox.io/s/react-leaflet-with-imageoverlay-add-remove-markers-dynamically-2tmex
Map Container
import React, { useEffect, useState } from "react";
import "leaflet/dist/leaflet.css";
import L from "leaflet";
import masterplan from "./img/masterplan.jpg";
let map;
const markerIcon = 'https://www.pinclipart.com/picdir/big/141-1410207_marker-filled-icon-location-logo-png-file-clipart.png'
const editMarkerIcon = 'https://cdn.iconscout.com/icon/premium/png-256-thumb/marker-1829264-1551716.png'
var myIcon = L.icon( {
iconUrl: markerIcon,
iconSize: [ 30, 35 ],
iconAnchor: [ 15, 35 ],
popupAnchor: [ 0, -40 ],
} );
var editMyIcon = L.icon( {
iconUrl: editMarkerIcon,
iconSize: [ 50, 40 ],
iconAnchor: [ 25, 35 ],
popupAnchor: [ 0, -40 ],
} );
const MapContainer = ( { data, linkName, formCss, addMarker, isMarkerAdded, setIsMarkerAdded, formData, setFormData, setGetMarkerObject, setMarkersList, setEdit, clicked, setClicked } ) => {
const [ coordinates, setCoordinates ] = useState( { lats: 0, lngs: 0 } )
// setting up the map with the image inside
const MaxMapMBounds = [ [ 0.047356142676201576,
-0.05648240856298255 ], [ -0.09911669921875, 0.05903846615873286 ] ]
// building the map container
const buildMap = () => {
map = L.map( 'map', {
crs: L.CRS.Simple,
maxBounds: MaxMapMBounds,
minZoom: 13,
maxZoom: 15,
maxBoundsViscosity: 0.0,
} ).setView( [ 0, 0 ], 13 );
// rendering the image inside the map
var imageBounds = [ [ 0.0484693481114499, -0.0652342559975141 ], [ -0.03143095517869242, 0.06518541415112039 ] ];
const image = L.imageOverlay(
masterplan,
imageBounds,
{ className: 'image-map' },
).addTo( map );
// Logging coordinates when clicking on the map
map.on( 'click', function ( e ) {
var coord = e.latlng;
var lat = coord.lat;
var lng = coord.lng;
console.log( `latitude: ${ lat } and longitude: ${ lng } === [${ lat } ,
${ lng } ]` );
} );
return map
}
useEffect( () => {
buildMap()
addMarkers()
renderMarkers()
return () => {
map.off();
map.remove();
}
}, [ data, ] );
// // adding markers from FormMarker with draggable markers available
const addMarkers = ( editedMarkerLatLng ) => {
if ( addMarker )
{
let marker;
// adding markers
if ( isMarkerAdded === false )
{
let onDrag = function ( e ) {
let latlng = marker.getLatLng();
setCoordinates( { lats: latlng.lat, lngs: latlng.lng } )
setFormData( { ...formData, coordinates: [ latlng.lat, latlng.lng ] } )
// showing popup when dragging
marker.bindPopup().openPopup()
};
let onClick = function ( e ) {
setIsMarkerAdded( true )
map.off( 'click', onClick ); //turn off listener for map click
marker = L.marker( e.latlng, { icon: editMyIcon, draggable: true } ).addTo( map ).bindPopup( '<p>انا هنا يا جون</p>', { closePopupOnClick: false, autoClose: false, closeOnClick: false, } ).openPopup();
// updating coordinates <p> on the ui
setCoordinates( { lats: e.latlng.lat, lngs: e.latlng.lng } )
// updating coordinates on the form
setFormData( { ...setFormData, coordinates: [ e.latlng.lat, e.latlng.lng ] } )
marker.on( 'drag', onDrag );
// removing popup after adding the marker to markersList
setGetMarkerObject( marker )
};
map.on( 'click', onClick );
}
}
}
// rendering Markers of the Units inside the map &&& Focusing on selected unit by touching on its button link
const renderMarkers = () => {
data && data.map( ( link ) => {
let marker
if ( addMarker )
{
// rendering markers at add-marker page
var container = L.DomUtil.create( 'div' );
const createButton = ( label, container, className ) => {
var btn = L.DomUtil.create( 'button', '', container );
btn.setAttribute( 'type', 'button' );
btn.setAttribute( 'class', className );
btn.innerHTML = label;
return btn;
}
const createParagraph = ( label, container, className ) => {
var para = L.DomUtil.create( 'p', '', container );
para.setAttribute( 'class', className );
para.innerHTML = label;
return para;
}
const editButton = createButton( 'Edit marker', container, 'edit-button' );
const removeButton = createButton( 'Remove marker', container, 'remove-button' );
const popupText = createParagraph( `Name: ${ link.name }`, container, 'popup-text' )
L.DomEvent.on( editButton, 'click', () => handleEditMarker( marker ) );
L.DomEvent.on( removeButton, 'click', () => handleRemoveMarker( marker ) );
return marker = L.marker( L.latLng( link.markerPosition ), { icon: myIcon } ).addTo( map ).bindPopup( container )
}
// rendering markers at home page
marker = L.marker( L.latLng( link.markerPosition ), { icon: myIcon } ).addTo( map ).bindPopup( link.markerText )
if ( link.name === linkName )
{
// Focusing on selected unit by touching on its button link
let unitMarker = L.polygon( link.latlng, { fill: false, opacity: 0 } ).addTo( map )
marker.openPopup()
map.fitBounds( unitMarker.getBounds() )
// setClicked( true )
}
} )
}
const handleEditMarker = ( marker ) => {
setIsMarkerAdded( true )
setEdit( true )
const lat = marker._latlng.lat
const lng = marker._latlng.lng
let editedMarker = data.find( item => item.markerPosition[ 0 ] === lat && item.markerPosition[ 1 ] === lng )
handleRemoveMarker( marker )
setFormData( { ...editedMarker, coordinates: [ lat, lng ] } )
marker = L.marker( [ lat, lng ], { icon: editMyIcon, draggable: true } ).addTo( map ).bindPopup( '<p>انا هنا يا جون</p>', { closePopupOnClick: false, autoClose: false, closeOnClick: false, } ).openPopup();
let onDrag = function ( e ) {
let latlng = marker.getLatLng();
// setCoordinates( { lats: latlng.lat, lngs: latlng.lng } )
setFormData( { ...editedMarker, coordinates: [ latlng.lat, latlng.lng ] } )
// showing popup when dragging
marker.bindPopup().openPopup()
};
marker.on( 'drag', onDrag );
setGetMarkerObject( marker )
}
const handleRemoveMarker = ( marker ) => {
const lat = marker._latlng.lat
const lng = marker._latlng.lng
let tempMarkers = data.filter( item => item.markerPosition[ 0 ] !== lat && item.markerPosition[ 1 ] !== lng )
setMarkersList( tempMarkers )
}
return (
<div className={ formCss ? 'map-container-form' : 'map-container' }>
<div id="map"
></div>
{ formCss &&
<p>coordinates: { `[ ${ coordinates.lats }, ${ coordinates.lngs } ]` }</p>
}
</div>
);
};
export default MapContainer
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Accordion Component
import React, { useEffect } from 'react'
import { Accordion, useAccordionButton } from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
const AccordionComp = ( { link, handleLink } ) => {
return (
<Accordion.Item eventKey={ link.id } onClick={ ( e ) => {
handleLink( e, link.name )
} } >
<Accordion.Header >
{ link.name }
</Accordion.Header>
<Accordion.Body>
<p><b> Type: </b>{ link.type }</p>
<p><b> Area: </b>{ link.area } ( ㎡ )</p>
<p><b> Price: </b> { link.price } (LE)</p>
<p><b> Description: </b> { link.description }</p>
</Accordion.Body>
</Accordion.Item>
)
}
export default AccordionComp
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
App Component
**the problem is when i click on the accordion, 'handleLink' fn is invoked to update linkName state using setLinkName.... when i comment out the setLinkName fn => accordion worked from the first click :( **
import './App.css';
import React, { useState, useEffect } from 'react'
import { data } from './data'
import MapContainer from './MapContainer'
import { Route, Switch } from "react-router-dom";
import FormMarker from './FormMarker';
import AccordionComp from './components/AccordionComp';
import { Accordion } from 'react-bootstrap';
const App = () => {
const [ clicked, setClicked ] = useState( false )
const [ linkName, setLinkName ] = useState( null )
const initialMarkersList = localStorage.getItem( "markersList" )
? JSON.parse( localStorage.getItem( "markersList" ) )
: [];
const handleLink = ( e, linkName ) => {
setLinkName( linkName )
}
const Home = () => {
return (
<div className="App">
<div className="links">
<Accordion >
{ initialMarkersList && initialMarkersList.map( ( link ) => {
return <AccordionComp key={ link.id } link={ link } setLinkName={ setLinkName } handleLink={ handleLink } setClicked={ setClicked } />
} ) }
</Accordion>
</div>
<MapContainer data={ initialMarkersList } linkName={ linkName } clicked={ clicked } setClicked={ setClicked } />
</div>
)
}
useEffect( () => {
console.log( document.getElementsByClassName( 'accordion-collapse' )[ 0 ].classList );
}, [] )
console.log( linkName, 'linkname' );
console.log( clicked, 'clickde' );
return (
<>
<Switch>
<Route path="/" exact component={ Home } />
<Route path="/add-marker" exact component={ FormMarker } />
</Switch >
</>
)
}
export default App;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>