0

I have added an event listener to "keydown" event in my ReactJS application. Because of a third party library, I'm adding it via setTimeout in a useEffect, this component is like a popup, so it mounts-unmounts lots of times in the app lifecycle. Its like below;

      useEffect(() => {
        const timeoutID = setTimeout(() => {
          clearTimeout(timeoutID);
            window.addEventListener("keydown", theFunction, true);
          
        }, 500);
        
        function theFunction(event: KeyboardEvent) {
           // do something
        }

        return () => {
          window.removeEventListener("keydown", theFunction, true);
        };
      }, []);

It's a typical usage, to remove event listener using removeEventListener method. The problem is, when the component unMounts, the event stays and stacks up on top of another. I'm actually seeing it on the Event Listeners tab on Devtools.

I was suspecting that, every time this component mounts, theFunction changes in the memory, so it's not the same function and therefore React can't remove it. I took theFunction from the useEffect and put it to the top of the component (outside of the functional component definition, just below the imports).

// imports

function theFunction(event: KeyboardEvent) {
               // do something
            }

const Component = () => {
          useEffect(() => {
            const timeoutID = setTimeout(() => {
              clearTimeout(timeoutID);
                window.addEventListener("keydown", theFunction, true);
              
            }, 500);
            
            return () => {
              window.removeEventListener("keydown", theFunction, true);
            };
          }, []);

And voila! The event is now adding just once and does not stack.

I'm not sure is it really beacuse theFunction was not same each time (as I thought) the component unMounts, or is there another React thing that I'm missing. Why React does not removes this eventListener on unMount in this case?

Edit: React version: 16, strict mode is on

2
  • Plese use a website like codesandbox to create a demo. I was not able to reproduce your issue. Aslo please mention details like react version and whether strict mode is used Commented Jul 8 at 6:59
  • I just added the version etc. Commented Jul 8 at 7:07

1 Answer 1

1

That this works with theFunction defined outside the component scope can indicate underlying problem. It's revealed in strict mode because it causes an immediate clean up of the effect, which probably would not happen under normal circumstances.

clearTimeout inside setTimeout doesn't serve a good purpose because a timeout has already occurred, but it's necessary on cleanup to avoid race condition that supposedly occurs when a listener that has not been added yet is removed:

  useEffect(() => {
    const timeoutID = setTimeout(() => {
        window.addEventListener("keydown", theFunction, true);          
    }, 500);
    
    function theFunction(event: KeyboardEvent) {
       // do something
    }

    return () => {
      clearTimeout(timeoutID);
      window.removeEventListener("keydown", theFunction, true);
    };
  }, []);
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.