React keydown Event Listener is being called multiple times?

Viewed 2625

So I made this modal https://codesandbox.io/s/amazing-morning-ukxp2?file=/src/components/Modal.js

Whenever you press the esc key, it is suppose to close the modal. I added a message to show each time I press esc.

But if you check the console it prints my messages in multiples of 2x each time, so after a while it will say the message like 100 times

This is the function I'm referring to

           function keyPress(e) {
        if (e.key === 'Escape' && showModal) {
          setShowModal(false);
          console.log('I pressed');
        }
      }

      document.addEventListener('keydown', keyPress);

I tried to put it into a useEffect function and add showModal as a dependency, but I couldn't get it to work properly

How would I prevent the keydown event listener from showing so many times in the console when I only want it to trigger when the modal is open and when I press the esc key once?

It seems like every time I re open the modal, it doubles the esc key message.

2 Answers

You need to handle the event listeners (add, removing) inside useEffect hook:

useEffect(() => {
  document.addEventListener("keydown", keyPress);
  return () => document.removeEventListener("keydown", keyPress);
});

This way, you will subscribe to keyPress function in every lifecycle. To improve it further, you could wrap keyPress in useCallback hook (to memoize the function in new renders) and add it as a dependency:

  const keyPress = useCallback(
    (e) => {
      if (e.key === "Escape" && showModal) {
        setShowModal(false);
        console.log("I pressed");
      }
    },
    [setShowModal, showModal]
  );

  useEffect(() => {
    document.addEventListener("keydown", keyPress);
    return () => document.removeEventListener("keydown", keyPress);
  }, [keyPress]);

Modified codesandbox

 useEffect(()=>{
        document.addEventListener('keydown', someFunction); 
    }, []);

the empty array at the end will till react to not refresh the component once loaded

Related