Search code examples
reactjsreact-hooksuse-effectevent-listener

trying to an remove event listeners on state change


I'm trying to remove a mouseover/deviceorientatation event listener when a click on an object when it changes state.

I can addeventlisteners on load and when the state equals false.

I've added console.logs to make sure the useEffect gets to each part of the if statement, which it does, but I can't seem to remove the event listeners when the state is equal to true.

I understand that I need to have it return for clean-up, but no matter how much I try I cant remove any eventlistener.

I've added my useEffect code below, if you need any more of the code please let me know and I'll add it to the question.

Any help would be much appreciated.

  useEffect(() => {
const noHover = window.matchMedia("(hover: none)");
if(!isOpen) {
  if(noHover.matches) {
    console.log('one')
    window.addEventListener('deviceorientation', (event) => DeviceOrientation(event), true);
  } else {
    console.log('two')
    window.addEventListener('mousemove', (event) => MouseOrientation(event), true);
  }
} else {
  if(noHover.matches) {
    console.log('three')
    window.removeEventListener('mousemove', (event) => MouseOrientation(event), true);
  } else {
    console.log('four')
    window.removeEventListener('deviceorientation', (event) => DeviceOrientation(event), true);
  }
}

return () => {
  window.removeEventListener('mousemove', (event) => MouseOrientation(event), true);
  window.removeEventListener('deviceorientation', (event) => DeviceOrientation(event), true);
}  }, [isOpen]);

Solution

  • The useEffect returns a cleanup function that is called whenever the dependencies (isOpen) change and can remove the event handlers. However, the event handlers need to be the actual event handler that was used in addEventListener, not just an identical function.

    You can achieve it by assigning the functions to variables according to your rules, and only adding/removing the listeners if the variable has been assigned a function.

    useEffect(() => {
      let deviceorientationHandler = null;
      let mousemoveHandler = null;
     
      if (!isOpen) {
        const noHover = window.matchMedia("(hover: none)");
        if (noHover.matches) {
          deviceorientationHandler = (event) => DeviceOrientation(event);
        } else {
          mousemoveHandler = (event) => MouseOrientation(event);
        }
      }
      
      if(deviceorientationHandler) window.addEventListener('deviceorientation', deviceorientationHandler, true);
      
      if(mousemoveHandler) window.addEventListener('mousemove', mousemoveHandler, true);
    
      return () => {
        if(deviceorientationHandler) window.removeEventListener('mousemove', deviceorientationHandler, true);
        
        if(mousemoveHandler) window.removeEventListener('deviceorientation', mousemoveHandler, true);
      }
    }, [isOpen]);
    

    We can shorten it a bit more by assigning the entire event params to a variable (handler), and only adding or removing the event if handler has a value:

    useEffect(() => {
      let handler = null
    
      if (!isOpen) {
        const noHover = window.matchMedia("(hover: none)");
    
        handler = noHover.matches ?
          ['deviceorientation', (event) => DeviceOrientation(event), true] :
          ['mousemove', (event) => MouseOrientation(event), true];
          
        window.addEventListener(...handler);
      }
    
      return () => {
        if (handler) window.removeEventListener(...handler);
      }
    }, [isOpen]);