skip to Main Content

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>
  ) : <></>
  )
};

2

Answers


  1. 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

    Login or Signup to reply.
  2. You could use <Marker /> component with onClick props from react-google-maps/api library

    What @Yilmaz said is correct, but since you are also using react-google-maps/api library, it would be much simpler for you to use <Marker /> component since it has an onClick prop.

    ref: https://react-google-maps-api-docs.netlify.app/#marker

    Here’s what you can do:

    Create a changeState function to toggle the state between true and false using ternary operator (This is just for testing).

    const changeState = () => {
        state === false ? setState(true) : setState(false);
      };
    

    Create a markerClicked function to put on your <Marker /> onClick props.

    const markerClicked = (e) => {
        // You can use the 'e' parameter if you want to get the event of the click.
        // For now, we'll just log the current state per marker click
        console.log("Marker clicked - State is : " + state);
      };
    

    Then here’s what your components should look like along with these changes:

      return isLoaded ? (
        <Box width="100%" height="100%">
          <GoogleMap
            mapContainerStyle={{ position: "fixed", height: "100%", width: "100%" }}
            center={location}
            zoom={15}
            onLoad={onLoad}
          >
            {/* Here I used the <Marker /> component instead of the vanilla way
            of rendering a marker on the map */}
            <Marker position={location} onClick={markerClicked} />
          </GoogleMap>
          <Box
            sx={{
              position: "fixed",
              top: 200,
              right: 200,
              width: 50,
              height: 50,
              backgroundColor: "black"
            }}
            onClick={changeState}
          />
        </Box>
      ) : (
        <></>
      );
    

    With this, when the map gets loaded, it logs useEffect - State is : false since that was your default state value. Then when you click on the marker, it will also log the current state Marker clicked - State is : false.

    To test if it is working properly, you can toggle the state between true and false by clicking the box, then clicking the marker after it to see if the returned state is the same.

    Here’s a working sandbox for you to test for yourself:

    https://codesandbox.io/s/proof-of-concept-sandbox-log-the-state-per-marker-click-and-change-state-per-box-click-ok4vc0?file=/src/Map.js

    Hope this helps.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search