Search code examples
javascriptreactjsaddeventlisteneruse-effectremoveeventlistener

Why is the window.eventListener not being removed in my React code, although I am calling it?


I am working on the following code:

  const [userNick, setUserNick] = useState("");
  const [eventListenerActive, setEventListenerActive] = useState(false);

  const promptOnLeaving = (ev) => {
      ev.returnValue = true;
  }

  useEffect(() => {
    if(userNick !== "" && !eventListenerActive){
      console.log("ADDING EVENT LISTENER")
      window.addEventListener('beforeunload', promptOnLeaving)
      setEventListenerActive(true);
    }
    else if(userNick === "" && eventListenerActive){
      console.log("REMOVING EVENT LISTENER")
      window.removeEventListener('beforeunload', promptOnLeaving)
      setEventListenerActive(false)
    }
  }, [userNick])

I want to add an event listener to my window, that listens on the beforeunloading-event, e.g the side will be closed, redirected or reloaded.

Since there are multiple input fields in the UI, I want to ask the user if he is sure he wants to close the page, although there are some form fields open. One of them is the user nickname (here userNick).

When the user nick is empty, I want to remove the event listener, since there isn't any data that would be lost on a reload, so I, therefore, don't need to prompt the user.

On the other hand, if the user has entered text inside the UserNick input field, I want to add the event listener and prompt him, if he is sure he wants to close the page.

My problem here is, that the event listener doesn't get removed when the user empties the input field.

Adding the listener works without a problem and the listener itself does what it should. But I can't seem to remove the listener properly.

When the else if block gets executed, my eventListenerActive state gets reset, and on the next user input there will be another eventListener making it 2 already.

I tried to add and remove the evenListeners through external buttons with the onClick-event, and that did actually work.

Additionally, I also read that the event-handling function should not be anonymous, when I want to remove it, so I gave it a name here to.

So what am I doing wrong here, that the eventListener won't be removed?

Thanks in advance


Solution

  • I figured it out myself. This article was very helpful: Removing event handlers in React

    The actual problem was, that javascript internally compares the handler function when the listener was added with the handler function when the listener is being removed. Since React re-renders the component, the handler function gets initialized again which results in an unmatch between the "old" handler function and the "new" one.

    I had to add a useCallback-function to my handler, so the handler get memoized and stays the same when compared on removeEventListener:

      const promptOnLeaving = useCallback((ev) => { 
        ev.returnValue = true;
      }, [])