sandy
sandy

Reputation: 303

React-Leaflet MapContainer is not rendering with new position value on state update

I'm a new leaflet user for my Typescript and Hook based React application. In my app my current geolocation position(latitude, longitude) is taken(I've allowed the permission to the browser) using the chrome browser's geolocation API by the app and should be shown in the map with a marker. The issue is, the map is always displayed with the initial default position([0,0]). That is, the new position update taken from geolocation API is not displayed.

The issue might be simple but I couldn't understand what I'm missing here that the Leaflet map is not taking my updated position. I checked, new position values are printed correctly, even I rendered just the updated position values inside a simple div, the new values are rendered correctly. Here is my code as below. Any help is much appreciated.

App.tsx

import React from 'react';
import './App.css';
import { Container } from '@material-ui/core';
import {Route,Switch} from 'react-router-dom';
import Header from './components/Header';
import SideBar from './components/SideBar';
import ShowInGoogleMap from './components/ShowInGoogleMap';
import ShowInLeafletMap from './components/ShowInLeafletMap';


function App() {
  return (
    <div className="app-div">
            <Header/>
            <div className="main-content">
              <SideBar/>
              <div className="map-content">
                <Switch>
                  <Route path="/google-map" component={ShowInGoogleMap}/>
                  <Route path="/leaflet-map" component={ShowInLeafletMap}/>
                </Switch>
              </div>  
            </div>
            

    </div>
    
    
  );
}

export default App;

###############################

ShowInLeafletMap.tsx - this is the function handling Leaflet map

import { LatLngExpression } from 'leaflet';
import React, { useState, useEffect } from 'react';
import 'leaflet/dist/leaflet.css';
import { MapContainer, Marker, Popup, TileLayer } from 'react-leaflet';

const ShowInLeafletMap = () => {
    const [position, setPosition] = useState<LatLngExpression>([0,0]);

    useEffect(() => {
      if ("geolocation" in navigator) {
        console.log("Available...");
        navigator.geolocation.getCurrentPosition(function(cPosition) {
          console.log("Latitude is :", cPosition.coords.latitude);
          console.log("Longitude is :", cPosition.coords.longitude);
          setPosition([cPosition.coords.latitude,cPosition.coords.longitude]);

        });
      } else {
        console.log("Not Available");
      }


    }, []);

    console.log('this is loaded, ', position);

return(
  
    <MapContainer center={position} zoom={13} scrollWheelZoom={false}>
    <TileLayer
      attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
      url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
    />
    <Marker position={position}>
      <Popup>
        Hi I just found Dresden
      </Popup>
    </Marker>
  </MapContainer>

    
  
)
} 

export default ShowInLeafletMap;

Here is dependency block of my package.json. I've no dev dependecies for the time being.

"dependencies": {
    "@material-ui/core": "^4.11.4",
    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/react": "^11.1.0",
    "@testing-library/user-event": "^12.1.10",
    "@types/googlemaps": "^3.43.3",
    "@types/jest": "^26.0.15",
    "@types/node": "^12.0.0",
    "@types/react": "^17.0.0",
    "@types/react-dom": "^17.0.0",
    "@types/react-leaflet": "^2.8.1",
    "@types/react-router-dom": "^5.1.7",
    "leaflet": "^1.7.1",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-leaflet": "^3.2.0",
    "react-router-dom": "^5.2.0",
    "react-scripts": "4.0.3",
    "typescript": "^4.1.2",
    "web-vitals": "^1.0.1"
  },

Upvotes: 2

Views: 2634

Answers (2)

Dima Dorogonov
Dima Dorogonov

Reputation: 2563

In leaflet v.4 I used this anti-pattern to rerender (wondering about their documentation):

<MapContainer key={new Date().getTime()} ...>

Upvotes: 0

Seth Lutske
Seth Lutske

Reputation: 10772

In react-leaflet version 3, most props are immutable. As for the MapContainer itself:

Except for its children, MapContainer props are immutable: changing them after they have been set a first time will have no effect on the Map instance or its container.

You're better off using the whenCreated prop to capture a reference to the underlying L.map instance in a state variable, then using leaflet methods directly:

const ShowInLeafletMap = () => {

  const [map, setMap] = useState<L.Map>()

  useEffect(() => {
    if ("geolocation" in navigator && map) {
      navigator.geolocation.getCurrentPosition(function(cPosition) {
      map.panTo([cPosition.coords.latitude,cPosition.coords.longitude]);
      });
    } 
  }, [map])

  return (
    <MapContainer whenCreated(setMap)>
      ...
    </MapContainer>
  )

}

Working codesandbox

Upvotes: 1

Related Questions