Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Socket disconnects before the onBeforeUnload event is fired. #1451

Closed
chuckntaylor opened this issue Mar 12, 2021 · 5 comments
Closed

Socket disconnects before the onBeforeUnload event is fired. #1451

chuckntaylor opened this issue Mar 12, 2021 · 5 comments
Labels
bug Something isn't working
Milestone

Comments

@chuckntaylor
Copy link

Describe the bug
I ensure that the user really meant to navigate away from the page by listening to the beforeUnload event on the window. However, before this is handled, the disconnect event is emitted before the user has a chance to make their choice of whether or not they want to stay on the page.

I would like to note that this issue does not happen when I downgrade to v2.

Stack where I have this issue:

  • socket.io-client ^3.1.2
  • socket.io 3.1.0
  • socket.io-redis ^6.0.1

Stack where this is not an issue:

  • socket.io-client 2.4.0
  • socket.io 2.4.1
  • socket.io-redis 5.4.0

Lastly, I am working within a Vue.js app, and so I do also work with the vue-socket.io package.

To Reproduce

window.addEventListener('beforeunload', (event) => {
      if (sessionHasStarted) {
        this.$socket.emit('test') // test emit to catch on the server. Does not appear on server - only the  disconnect event.
        event.preventDefault()
        event.returnValue = ''
      }
    })

Expected behavior
The disconnect should not trigger until a user confirms navigating away.

Platform:

  • Device: MacBook Pro 2017
  • OS: macOS Big Sur 11.2.3

Additional context
I found what appeared to be an old option

const socket = io(url, { 'sync disconnect on unload':false });

But this does not seem to work.

@darrachequesne
Copy link
Member

A temporary workaround is to call event.stopImmediatePropagation():

window.addEventListener('beforeunload', (event) => {
  if (sessionHasStarted) {
    this.$socket.emit('test') // test emit to catch on the server. Does not appear on server - only the  disconnect event.
    event.preventDefault()
    event.stopImmediatePropagation()
    event.returnValue = ''
  }
})

More information here: socketio/engine.io-client#658

@chuckntaylor
Copy link
Author

While I have since moved my entire project to v2 to resolve my issue, I should thank you for your suggestion. I will take a look at this. I was unaware of the event.stopImmediatePropagation() method.

darrachequesne added a commit to socketio/engine.io-client that referenced this issue May 4, 2021
Since [1], the socket is now closed when receiving the "beforeunload"
event in the browser.

This change was meant to fix a discrepancy between Chrome and Firefox
when the user reloads/closes a browser tab: Firefox would close the
connection (and emit a "disconnect" event, at the Socket.IO level), but
not Chrome (see [2]).

But it also closes the connection when there is another "beforeunload"
handler, for example when the user is prompted "are you sure you want
to leave this page?".

Note: calling "stopImmediatePropagation()" was a possible workaround:

```js
window.addEventListener('beforeunload', (event) => {
  event.preventDefault();
  event.stopImmediatePropagation();
  event.returnValue = 'are you sure you want to leave this page?';
});
```

This commit adds a "closeOnBeforeunload" option, which controls whether
a handler is registered for the "beforeunload" event.

Syntax:

```js
const socket = require('engine.io-client')('ws://localhost', {
  closeOnBeforeunload: false // defaults to true
});
```

[1]: ed48b5d
[2]: socketio/socket.io#3639

Related:

- #661
- #658
- socketio/socket.io-client#1451

Reference: https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
@darrachequesne
Copy link
Member

Starting with socket.io-client@4.1.0, you can now disable the "beforeunload" handler with the closeOnBeforeUnload option:

const socket = io(url, {
  closeOnBeforeunload: false
});

@darrachequesne darrachequesne added this to the 4.1.0 milestone May 17, 2021
@rickgbw
Copy link

rickgbw commented May 18, 2021

Using closeOnBeforeunload it solves the problem, however the server will take a long time to detect the user disconnection. Of course it is the side effect, but in 2.x versions this detection seems to be instantaneous, even without this "beforeunload" trigger.

@chuckntaylor
Copy link
Author

I have finally moved up to the latest versions of these packages while at the same time moving my project to Typescript.
I can confirm that with:

  • socket.io-client ^4.1.2
  • socket.io ^4.1.2
  • socket.io-redis 6.1.1

Using the closeOnBeforeunload: false option resolved my previous issues. Thank you kindly for this awesome package!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants