Search code examples
reactjsgoogle-maps-api-3

Setting state does not change the value in GoogleMaps addListener


Very simple case, but I can't find the way out!

I have a Google map with a single marker. The "click" listener callback function logs the state's value. I added a button that changes the state's value, and a useEffect hook that logs the state value when it is changed, just to check it has changed.

Problem: when I change the state value from "false" to "true" with the button, the hook is triggered and logs the new value ("true"), so I'm pretty sure the state has changed. BUT when I click on the marker, it still returns the previous value ("False") of the state.

How can I get the marker "click" callback to use the updated value of the state?

Thank you for your help, I'm stuck and I could find no support on this issue!

My code (a small and simplified extract of a bigger project!).

import { useEffect, useCallback, useState } from "react";
import { GoogleMap, useJsApiLoader } from "@react-google-maps/api";
import Box from '@mui/material/Box';

export default function MapPage() {
  const [state, setState] = useState(false)
  
  // GoogleMap loading with @react-google-maps/api
  const { isLoaded } = useJsApiLoader({
      id: 'google-map-script',
      googleMapsApiKey: "MY-API-KEY"
  })

  // Marker creation with officiel GoogleMaps API
  const onLoad = useCallback(function callback(map) {
    let marker = new window.google.maps.Marker({
      map: map,
      position: { lat: 43.3318, lng: 5.0550 },
      icon : { url : "/static/NotSubscribed.svg", scaledSize : {width:60, height:60}}
    });
    marker.addListener("click", () => {console.log("Marker clicked - State is : " + state)}); 
  }, [])

  // useEffect to display new state when value changed
  useEffect(() => {
    console.log("useEffect - State is : " + state)
  }, [state])

  return (isLoaded ? (
    <Box width="100%" height="100%">
      <GoogleMap 
        mapContainerStyle={{ position: "fixed", height:"100%", width: "100%"}}
        center={{ lat: 43.3318, lng: 5.0550 }}
        zoom={15}                
        onLoad={onLoad}
      >
      </GoogleMap>
      <Box sx={{position : "fixed", top : 200, right : 200, width : 50, height : 50, backgroundColor : "black"}}
          onClick={() => setState(true)}
        />
    </Box>
  ) : <></>
  )
};


Solution

  • const onLoad = useCallback(function callback(map) {
        let marker = new window.google.maps.Marker({
          map: map,
          position: { lat: 43.3318, lng: 5.0550 },
          icon : { url : "/static/NotSubscribed.svg", scaledSize : {width:60, height:60}}
        });
        marker.addListener("click", () => {console.log("Marker clicked - State is : " + state)}); 
      }, [])
    

    since dependency array is empty, this function is created once and reads state when the function is built. Although, you are adding [state] to use useEffect, this rerenders the component but it does not re create the useCallback callback function. you have to add same dependency [state] to use useCallback