简体   繁体   中英

setState doesn't re-render react functional component

I am getting user's location and setting it as the new state at "onSuccess" function, the component doesn't re-render. After checking a lot i have seen that react doesn't see it as a change of state because it is an array in that case and it doesn't pass react's "equality" check as a state that was changed. With that, nothing that i have tried has worked. any ideas?

import { useEffect, useState } from "react";
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";

export default function Map() {
  const [location, setLocation] = useState([52.234, 13.413]);

  const onSuccess = (position) => {
    let userLocation = [position.coords.latitude, position.coords.longitude];
    setLocation([...userLocation]);
  };

  useEffect(() => {
    if (!("geolocation" in navigator)) {
      alert("no Geolocation available");
    }
    navigator.geolocation.getCurrentPosition(onSuccess);
  }, []);
  console.log(location);
  return (
    <>
      <MapContainer
        className="leaflet-map"
        center={location}
        zoom={11}
        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={[51.505, -0.09]}>
          <Popup>
            A pretty CSS3 popup. <br /> Easily customizable.
          </Popup>
        </Marker>
      </MapContainer>
    </>
  );
}

It looks like MapContainer does not recenter after mounting even if the center prop is changed as mentioned in the docs:

https://react-leaflet.js.org/docs/api-map

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 could force replacing the MapContainer by passing a key prop that you change whenever location changes, for example:

<MapContainer
    key={`${location[0]}-${location[1]}`}

Or investigating other options in react-leaflet such as useMap to get access to the Leaflet Map instance and calling map.setView(...) https://leafletjs.com/reference-1.7.1.html#map-setview

Are you able to confirm that onSuccess is called at all? It may be that getCurrentPosition is running into an error, so calling it with two arguments would be good:

navigator.geolocation.getCurrentPosition(onSuccess, onError);

You should also include onSuccess in the useEffect dependencies.

  useEffect(() => {
    if (!("geolocation" in navigator)) {
      alert("no Geolocation available");
    }
    navigator.geolocation.getCurrentPosition(onSuccess);
  }, [onSuccess]);

And to prevent multiple calls to getCurrentPosition due to onSuccess changing, you should also useCallback with the dependency on setLocation :

  const onSuccess = useCallback((position) => {
    let userLocation = [position.coords.latitude, position.coords.longitude];
    setLocation([...userLocation]);
  }, [setLocation]);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM