Jorge Monroy
Jorge Monroy

Reputation: 438

How to disable an event listener in mapbox?

I am trying to have control over event listeners over a layer with Mapbox on React. map.off is supossed to do the trick, but it is not removing the onclick event in a layer. https://docs.mapbox.com/mapbox-gl-js/api/map/#map#off

But I am doing something wrong so I can't manage to remove the event. Here is what I have done so far...

To add the event I do this

map.on('click', 'building_footprints_click', addBuildingPopUp)

And to try to remove it I tried:

map.off('click', 'building_footprints_click', addBuildingPopUp);

map.off('click', 'building_footprints_click');

map.off('click', addBuildingPopUp);

But none of them are working. I read that I have to send to off the instance of the on event. So I tried to:

let event = map.on('click', 'building_footprints_click', addBuildingPopUp)

and the same three off operations as above but they don't work either

map.off('click', 'building_footprints_click', event);

map.off('click', 'building_footprints_click');

map.off('click', event);

And also the listener function, I have tried with:

const addBuildingPopUp = (e) => {} 

and 
function addBuildingPopUp (e) {} 

and 
let addBuildingPopUp = function building (e) {}

Here is a basic Stackblitz with a example of the non working function https://stackblitz.com/edit/react-5maykf?file=src/App.js

https://react-5maykf.stackblitz.io/

Upvotes: 4

Views: 10533

Answers (8)

On React you need define your funcion outside of the component

const showLayerInfo = (e: MapMouseEvent & { features?: MapboxGeoJSONFeature[] | undefined; } & EventData) => {
    console.log(e)
}

export default function Map(props: MapProps) {
  const mapContainer = useRef<HTMLDivElement>(null)
  const map = useRef<mapboxgl.Map>()

  map.current.off("mouseenter", layerData.name, showLayerInfo)
  map.current.on("mouseenter", layerData.name, showLayerInfo)

   return <>
     <div
        ref={mapContainer}
        className="map-container"
        style={{width: "100%", height: "100vh"}}
      />
    </>

}

Upvotes: 0

pumpfriction
pumpfriction

Reputation: 1

I'll chip in as I had a similar problem this morning. The solution that I ended up taking was storing the function that I was passing to map.on and map.off inside a useCallback hook with empty brackets. Worked like a charm!

Upvotes: 0

I have done it in React. I was trying for days to do this. So in my case I wanted to remove an on click listener which would add some custom markers. Here is how I did it (for testing):

Attributes in File

const map = useRef(null);

map.current.on('click', (event) => addMarkerOnClick(event));
setTimeout(() => {
  console.log("Disabled")
  map.current.off('click', (event) => addMarkerOnClick(event));
}, 5000)

The above was how I had my code initially, it WONT work.

    map.current.on('click', addMarkerOnClick);
    setTimeout(() => {
      console.log("Disabled")
      map.current.off('click', addMarkerOnClick);
    }, 5000)

That's the solution ^

For none React users, I suggest this:

// Attributes
let map = null

    map.on('click', addMarkerOnClick);
    setTimeout(() => {
      console.log("Disabled")
      map.off('click', addMarkerOnClick);
    }, 5000)

Upvotes: 1

Michael Payne
Michael Payne

Reputation: 11

The Functions that you pass to the mapbox event Listener have to be exactly the same. Everytime a component renders in react, the function (although it stays the same) will be recognized by javascript as a new declared function. Therefore mapbox will "off" a listener, that doesn't event exist, because it's a function thats never been there. To solve it, you have to use React "useCallback". It ensures that functions remain the same over the course of the existence of the component

export default function MapListeners() {
  const mode = useRecoilValue(interactivityMode);
  const setModal = useSetRecoilState(modalState);

  const removeListeners = () => {
    cuMap.off("click", setAddBeaconModal);
  };

  const setAddBeaconModal = useCallback(
    // useCallback ensures the functions stays identical
    (e: mapboxgl.MapMouseEvent & mapboxgl.EventData) => {
      // const coordinates = e.lngLat;

      setModal({
        key: AddBeaconModal.toString(),
        body: {
          component: AddBeaconModal,
          props: { word: "irgendwas", title: "Irrr" },
        },
        title: "Add Beacon",
      });
    },
    []
  );

  const setMapListeners = () => {
    removeListeners();

    switch (mode) {
      case "add":
        cuMap.on("click", setAddBeaconModal);
        break;
      case "lock":
        break;
      default:
    }
  };

  useEffect(setMapListeners, [mode]);

  return null;
}

Upvotes: 1

Alexandr Bich
Alexandr Bich

Reputation: 41

If you use React, just store the handler(s) inside of useRef, and voila, everything will work, here's the similar situation and a proposed solution

Here's the code for the solution:

  const handleClickRef = useRef(handleClick)
  handleClickRef.current = handleClick
  map.on('mousedown', 'layer-id', handleClickRef.current)
  map.off('mousedown', 'layer-id', handleClickRef.current)

Upvotes: 0

pachonjcl
pachonjcl

Reputation: 741

Based on your stackblitz I can see that the problem is that you are trying to deregister a function that no longer exists.

For mapbox you need to register and deregister the same function. I mean map.on('mousemove', f1) and map.off('mousemove', f1) you need to ensure that f1 remains the same.

Let me explain that, whenever a react component renders, it does recreate all variables inside it's body unless it is part of a state variable, since the function addBuildingPopUp is created on every render mapboxgl does not deregister the event.

The only change you need to do is to ensure addBuildingPopUp remains the same, you should define outside React Component.

Upvotes: 6

Dolly
Dolly

Reputation: 2602

I checked your stackblitz code and update the working answer accordingly with comments. Let me know if you have any questions on it

https://stackblitz.com/edit/react-mqjjkg?devtoolsheight=33&file=src/App.js

Upvotes: 2

Alfred
Alfred

Reputation: 729

I don't know if you made a typo when writing this question but you wrote

map.on('click', 'building_footprints_click', addBuildingPop**UP**);
map.off('click', 'building_footprints_click', addBuildingPop**Up**); 

Notice the difference in case in the name of your function ?

This syntax is correct and it is the one you should use

If the problem was due to the typo I suggest using a linter to avoid this in the future

Upvotes: -3

Related Questions