Search code examples
google-mapsnext.jsgoogle-maps-api-3

Using maps with NextJS apps


I am working with a map inside a NextJS app, using GoogleMap. Below is the component displaying the map.

It mostly works as I expect.

function Map({latitude,longitude}:
             {latitude:number,longitude:number}) {
  const center = useMemo(() => ({
    lat: latitude,
    lng: longitude
  }), []);

  return (
    <GoogleMap zoom={17} center={center}
               mapContainerClassName="map-container">
      <Marker position={center} />
    </GoogleMap>
  );
} /* End of Map */

The component above is used inside this other component:

export default function GeoMap(
  {shop,gps}:
  {shop: string, gps: string;}
) {
  const [theGPS, setTheGPS] =
          useState({coords:{latitude:0.0,longitude:0.0}})
  const [theLatitude, setLatitude] = useState(0.0)
  const [theLongitude, setLongitude] = useState(0.0)
  const dbRef = ref(firebaseDatabase, 'MyApp/'+shop)

  useEffect(() => {
    onValue(dbRef, (snapshot) => {
      const coordsStr = snapshot.child('gPS').val().split(','),
      latit = parseFloat(coordsStr[0]),
      longit =  parseFloat(coordsStr[1])
      console.log('shop='+shop)
      console.log('coordsStr='+coordsStr)
      console.log('latit='+latit.toString())
      console.log('longit='+longit.toString())
      setTheGPS({coords: {latitude:latit, longitude:longit}})
      setLatitude(latit); setLongitude(longit)
    }, (error) => {
      console.log('Error(onValue): '+error);
    });
    }, [])

    useEffect(() => { // Not needed. Just for test.
      setLatitude(theGPS.coords.latitude);
      setLongitude(theGPS.coords.longitude)
    }, [theGPS])

    .....

  return <Map latitude= {theLatitude}
              longitude= {theLongitude} />
} /* End of GeoMap */

But I have the following issue:

When the values of theLatitude and theLongitude change (due to some external action) I can see it happen in the logs as expected, but the display in the map is not updated. Why is that?

If I do a manual page reloading, I can of course see the map updating adequately; but I want to have the map update itself without having to manually reload. Since theLatitude and theLongitude are state variables I would expect the map to automatically update itself.


Solution

  • I managed to reproduce the problem using a stub GoogleMap. The issue got resolved after adding dependencies to useMemo in the Map component:

    const center = useMemo(() => ({
      lat: latitude,
      lng: longitude
    }), [latitude, longitude]);
    

    Here is the rest of the code I used to reproduce:

    export function GeoMap() {
      const [theGPS, setTheGPS] =
              useState({coords:{latitude:0.0,longitude:0.0}})
      const [theLatitude, setLatitude] = useState(0.0)
      const [theLongitude, setLongitude] = useState(0.0)
    
        useEffect(() => { // Not needed. Just for test.
          setLatitude(theGPS.coords.latitude);
          setLongitude(theGPS.coords.longitude)
        }, [theGPS])
    
      return <div>
        <button onClick={() => {
          console.log('Clicked')
          setLatitude(theLatitude + 1)
          setLongitude(theLongitude + 1)
          
          }}>render</button>
        <Map latitude= {theLatitude}
                    longitude= {theLongitude} />
      </div>
    }
    
    function Map({latitude,longitude}:
      {latitude:number,longitude:number}) {
    
    
    // THE FIX
    const center = useMemo(() => ({
      lat: latitude,
      lng: longitude
    }), [latitude, longitude]);
    
    // PREVENTS RERENDER
    // const center = useMemo(() => ({
    // lat: latitude,
    // lng: longitude
    // }), []);
    
    return <GoogleMap center={center} zoom={17}/>
    }
    
    function GoogleMap({center, zoom }: {center: {lat:number,lng:number}, zoom: number}) {
      return <div>
        <div>{center.lat}</div>
        <div>{center.lng}</div>
      </div>
    }