5

I have a Backdrop_DIV which is rendered based on a open for a Dropdown component.

{open &&
  <LS.Backdrop_DIV
    onClick={handleBackdropClick}
    ref={backdrop_ref}
  >
    Backdrop
  </LS.Backdrop_DIV>
}

I want the Backdrop_DIV to go away if the user scrolls (touchmove).

Obs: This is a mobile view.

const handleTouchMove = useCallback(()=>{
    setOpen(false);
  },[]);
useEffect(() => {
    if (open) {
      // ATTACHING THE EVENT LISTENR
      backdrop_ref.current.addEventListener('touchmove', handleTouchMove );
    }

    // ATTEMPT TO REMOVE THE EVENT LISTENER
    return () => 
      backdrop_ref.current.removeEventListener('touchmove', handleTouchMove);

},[open,handleScroll]);

It works, but if fails when I'm trying to clear the event listener in the return of my useEffect. Is there a way to do this?

Error:

react-dom.development.js:20313 Uncaught TypeError: Cannot read property 'removeEventListener' of null

This error is pretty obvious, because the Backdrop_DIV is no longer mounted when it runs.

QUESTION

Do I need to bother removing the event listener in this case? What can I do?

cbdeveloper
  • 27,898
  • 37
  • 155
  • 336
  • Have you tried to use `componentWillUnmount`? – mindmaster Sep 18 '19 at 09:44
  • @mindmaster I'm using React Hooks and functional components only. There's no `componentWillUnmount` available. Thanks! – cbdeveloper Sep 18 '19 at 09:48
  • is open no longer true when you attempt to remove the event listener? If it isn't then it looks like LS.Backgrop_div is no longer being displayed, this is perhaps why backdrop.current is no longer defined. If the DOM element no longer exists, should you also remove the event listener, I am not sure, perhaps this might help https://stackoverflow.com/questions/12528049/if-a-dom-element-is-removed-are-its-listeners-also-removed-from-memory – Carlene Sep 18 '19 at 10:20
  • np @cbdev420 :) should I post the above as an answer to your question or do you need further help? – Carlene Sep 18 '19 at 10:37

1 Answers1

0

From question recommended by Carlene in the comments, I would say that it's not necessary to remove the event listener, since it will be handled by the garbage Collector, although some older browser might leak memory in this situation.

If a DOM Element is removed, are its listeners also removed from memory?

But also, I found a way to remove the listener before dismounting the Backdrop_DIV and it works:

  • I added a lastOpenState_ref useRef() keep track of the previous render open state so I can detect the render that will dismount the Backdrop_DIV (open === false) and I remove the listener during that render.
import React, {useState, useEffect, useRef, useCallback} from 'react';

function MyComponent() {

  const [open,setOpen] = useState(false);
  const backdrop_ref = useRef(null);
  const lastOpenState_ref = useRef(false);

  const handleBackdropTouchMove = useCallback(() => {
    setOpen(false);
  },[]);

  // BLOCK TO REMOVE EVENT LISTENER FROM backdrop_ref
  // SINCE IT'S IMPOSSIBLE TO REMOVE FROM THE useEffect RETURN
  // BECAUSE THE backdrop_ref IS NULL WHEN IT RUNS

  if (lastOpenState_ref.current === true && open === false) {
    backdrop_ref.current.removeEventListener('touchmove', handleBackdropTouchMove);
  }
  lastOpenState_ref.current = open;

  // EFFECT TO ATTACH 'touchmove' EVENT LISTENER ON 'backdrop_ref.current'

  useEffect(() => {
    if (open) {
      backdrop_ref.current.addEventListener('touchmove', handleBackdropTouchMove);
    }
  },[open,handleBackdropTouchMove]);

  // RETURN STATEMENT

  return(
    <LS.Container_DIV>
      {open &&
        <LS.Backdrop_DIV
          onClick={handleBackdropClick}
          ref={backdrop_ref}
        >
        </LS.Backdrop_DIV>
      }
      <SomeOtherStuff/>
    </LS.Container_DIV>
  );
}
cbdeveloper
  • 27,898
  • 37
  • 155
  • 336