Leaflet React get map instance in functional component
Asked Answered
F

4

15

I want to have a button outside the map that changes the view to another coordinates.

Is there any way to get mapContainer instance to call their functions? Or how can I implement that function?

I tried to get it by using ref, but it's not working. Here is my current code

const zoom = 13;

function Map({ regionCoord, regionName }) {

    const mapRef = useRef();

    function handleFlyToClick() {
      // This don't work
      // const map = mapRef.current.leafletElement 
      // map.flyTo(regionCoord, zoom)
    }

 return (   
        <React.Fragment>
            <Grid container >
                <Grid item xs={10}>
                    {regionCoord && <MapContainer
                        ref={mapRef}                     
                        center={[50,50]} 
                        zoom={zoom}                    
                        >
                        <TileLayer
                            attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
                            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                        />            
   
                        <Marker position={regionCoord}>
                          <Popup>{regionName}</Popup>
                        </Marker>        
                    </MapContainer>}                               
                </Grid>
                <Grid item xs={2}>
                    <button onClick={handleFlyToClick}>Fly To</button>
                </Grid>
            </Grid>
        </React.Fragment>  
    )
    
}

export default Map

I'm using react-leaflet v3

Finical answered 21/12, 2020 at 14:6 Comment(0)
M
23

You need to use a component which will include your button inside. To take the map instance use whenCreated prop of MapContainer. I think mapRef is not valid anymore with the latest version.

MapContainer:

 const [map, setMap] = useState(null);

 <MapContainer
      center={[50, 50]}
      zoom={zoom}
      style={{ height: "90vh" }}
      whenCreated={setMap}
  >
...

</MapContainer>
<FlyToButton />  // use the button here outside of the MapContainer

....

Create the component with the button and its event

function FlyToButton() {
  const onClick = () => map.flyTo(regionCoord, zoom);
    
  return <button onClick={onClick}>Add marker on click</button>;
}

Demo

Multiracial answered 21/12, 2020 at 14:36 Comment(7)
Struggeld to find how to get the instace of the map (whenCreated={setMap}) saved my life. ThanksPeroration
Wish this was in the documentation!Dimorphism
whenCreated does not appear to exist in react-leaflet 4.xTouzle
This answer was for react-leaflet v.3.x. It is clearly mentioned in the question.Multiracial
@Multiracial would you not then consider it a duplicate if someone posted the same exact question for 4.x?Touzle
It is not since the api changes all the time so it requires a different answer every time, that's the reason I guess for making different tags for react-leaflet.Multiracial
Could be wrong but whenReady={setMap} seems to be working for meClonus
J
12

whenCreated doesn't exist anymore on 4.0 but there is ref :

MapContainer:

const [map, setMap] = useState<Map|null>(null);
    
<MapContainer
    center={[50, 50]}
    zoom={zoom}
    style={{ height: "90vh" }}
    ref={setMap}
>
    <div> 
        ... do whatever you want with map
    </div>
</MapContainer>
Janettajanette answered 29/5, 2022 at 21:27 Comment(1)
correct answer as of latest version of react leafletVito
T
2

Works for both 3.x and 4.x:

Another method is to use the useMap hook, which exists in 3.x and 4.x. In order to use it in the parent component, you just need to add a child component, e.g. MapController below.

const MapController = () => {
  const map = useMap();

  // do something with map, in a useEffect hook, for example.

  return <></>;
};

 <MapContainer
      center={[50, 50]}
      zoom={zoom}
      style={{ height: "90vh" }}
  >
   <MapController />
   <div> 
     ... do whatever you want with `map`
   </div
</MapContainer>
Touzle answered 14/7, 2022 at 14:52 Comment(0)
L
0

you need to access the map element(from the map component which is the container not MapContainer), this is a very simple example:

export default function MapComponent() {
    const [mapCenter,setMapCenter] = useState([13.1538432,30.2154278])
    let [zoom,setZoomLevel] = useState(15)
    let [tile,setTile] = useState('https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png')
    
    let mapRef = useRef();

    const fly=()=>{
        mapRef.current.leafletElement.flyTo([14,30],15)
    }

    return (
    <>
        <button onClick={fly}>Click</button>
        <Map center={mapCenter} zoom={zoom} ref={mapRef} style={{width:'100%',height:'100%'}}>
            <TileLayer url={tile}/>
        </Map>
    </>  
    )
}
Lamppost answered 21/12, 2020 at 14:55 Comment(2)
i saw @Multiracial answer after i posted this, note i'm using react-leaflet 2.8.0.Lamppost
This does not work on Leaflet 3.0 anymore. The way is as described by the accepted answer here, using <MapContainer center={[50, 50]} zoom={zoom} style={{ height: "90vh" }} whenCreated={setMap} >Coverley

© 2022 - 2024 — McMap. All rights reserved.