Removing the beforeunload event does not work in React

29 Views Asked by At

The snippet below is suppose to add and remove event listener handleBeforeUnload. On checking the browser console I see the "inside else" getting printed however loading the page still throws the alert.

Any ideas why the event listener might not be getting removed?

useEffect(() => {
    const handleBeforeUnload = (e) => {
      e.preventDefault();
      const message =
        'Are you sure you want to leave? All provided data will be lost.';
      e.returnValue = message;
      return message;
    };

    if (condition1 && condition2) {
      console.log('inside if');
      window.addEventListener('beforeunload', handleBeforeUnload);
    } else {
      console.log('inside else');
      window.removeEventListener('beforeunload', handleBeforeUnload);
    }
  }, [stateVariable1, stateVariable2, stateVariable3]);

Additionally, is there any alternative to detecting a reload other than using beforeunload event? Since the returnValue property is deprecated the default message is really unhelpful. How can one detect and create custom alert messages?

I still see the popup even when the console prints the else statement, which means ideally the event listener ought to have been removed.

1

There are 1 best solutions below

0
adsy On

It's because the event listener function handleBeforeUnload is defined inside the effect. So when the effect runs again to remove it, the handleBeforeUnload function is re-created and this is not referentially equivalent to the one that was being used when the effect ran to register the event listener.

To fix this you need to return a cleanup function from the effect. The cleanup function that is returned will be executed by react just before the next effect execution runs. Since that closure has a reference to the exact (referentially the same) handleBeforeUnload that was used to register it from that effect execution, it will work.

When the condition evaluates to false, the cleanup function for the previously registered handler will be removed, and a new one wont be registered since its guarded by that condition.

useEffect(() => {
    const handleBeforeUnload = (e) => {
      e.preventDefault();
      const message =
        'Are you sure you want to leave? All provided data will be lost.';
      e.returnValue = message;
      return message;
    };

    if (condition1 && condition2) {
      window.addEventListener('beforeunload', handleBeforeUnload);
    }

    return () => {
        window.removeEventListener('beforeunload', handleBeforeUnload);
    }

  }, [stateVariable1, stateVariable2, stateVariable3]);

Additionally, is there any alternative to detecting a reload other than using beforeunload event?

No there is not. It's very purposefully limited because blocking the user from leaving has to be tightly controlled in the web standards, as it could be abused.