Búsqueda de sitios web

typescript - MapContainer de React-Leaflet no se está renderizando con el nuevo valor de posición al actualizar el estado


Soy un nuevo usuario de Leaflet para mi aplicación React basada en Typescript y Hooks. En mi aplicación, la posición geográfica actual (latitud, longitud) se obtiene (he permitido el permiso en el navegador) usando la API de geolocalización del navegador Chrome por la aplicación y debería mostrarse en el mapa con un marcador. El problema es que el mapa siempre se muestra con la posición inicial por defecto ([0,0]). Es decir, la nueva actualización de posición obtenida de la API de geolocalización no se muestra.

El problema podría ser simple, pero no pude entender qué me estaba perdiendo aquí para que el mapa de Leaflet no esté tomando mi posición actualizada. Verifiqué, los nuevos valores de posición se imprimen correctamente, incluso rendericé solo los valores de posición actualizados dentro de un simple div, los nuevos valores se muestran correctamente. Aquí está mi código a continuación. Cualquier ayuda es muy apreciada.

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 - esta es la función que maneja el mapa de Leaflet

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;

Aquí está el bloque de dependencias de mi package.json. No tengo dependencias de desarrollo por el momento.

"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"
  },

Respuesta #1

En react-leaflet versión 3, la mayoría de las propiedades son inmutables. En cuanto al propio MapContainer:

Excepto por sus hijos, las propiedades de MapContainer son inmutables: cambiarlas después de haber sido establecidas por primera vez no tendrá ningún efecto en la instancia del mapa o en su contenedor.

Es mejor usar la propiedad whenCreated para capturar una referencia a la instancia subyacente de L.map en una variable de estado, y luego usar los métodos de Leaflet directamente:

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>
  )

}

CodeSandbox funcionando

Respuesta #2

En leaflet v.4 usé este anti-patrón para volver a renderizar (preguntándome sobre su documentación):

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