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

fix(remix-react): Firefox LiveReload #4725

Merged
merged 1 commit into from Nov 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/thin-oranges-boil.md
@@ -0,0 +1,15 @@
---
"@remix-run/react": patch
---

Fixed a problem with live reload and firefox infinitely reloading the page

The problem is:

1. Firefox is calling `ws.onclose` immediately upon connecting (?!)
2. Then we’re trying to reconnect, and upon reconnection, we reload the page.
3. Firefox then calls `ws.onclose` again after reconnecting and the loop starts over

This fix is to check `event.code === 1006` before actually trying to reconnect and the reload the page. 1006 means the connection was closed abnormally (https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1). In our case, that means the server was shut down in local dev and then the socket can reconnect again when the server is back up.

It’s unclear to me why Firefox is calling `onclose` immediately upon connecting to the web socket, but it does.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi. When I first had this issue I did some digging and might have an idea of what’s happening:

  1. files change
  2. [Chrome, Firefox] websocket receives message type: RELOAD, triggers window.location.reload
  3. [Chrome, Firefox] start reload Navigated to http://localhost:3000/
  4. [Firefox] closes the websocket connection code: 1001
  5. [Firefox] sets timeout to call remixLiveReloadConnect
  6. [Firefox] timeout completes before reload is complete and calls:
    remixLiveReloadConnect({ onOpen() { /* … */ } })
  7. [Firefox] re-connects to the websocket, calls onOpen method that triggers another window.location.reload starting the infinite reloading

My conclusion is Firefox does not in fact call onclose upon connecting. It is the other way around, the connection is closed and tries to reconnect before the browser fully unloads the page. Both Chrome and Safari seem to only send the websocket close event when the dev server is stopped and not when the page reloads.

The reason why the onOpen config was introduced was to enable automatic re-connection when the dev server is stopped and restarted. When that happens the CloseEvent is sent with code 1006 which is why both of us came up with the event.code === 1006 check.

This fixes the infinite reloads. But the issue on Firefox is actually a race condition. When the timeout is increased (e.g. to 10000) the page reload reliably finishes before the timeout re-initializes, re-connects to the websocket and the reloads loop kicks in.

21 changes: 11 additions & 10 deletions packages/remix-react/components.tsx
Expand Up @@ -1646,7 +1646,6 @@ export const LiveReload =
let socketPath = protocol + "//" + host + ":" + ${String(
port
)} + "/socket";

let ws = new WebSocket(socketPath);
ws.onmessage = (message) => {
let event = JSON.parse(message.data);
Expand All @@ -1663,15 +1662,17 @@ export const LiveReload =
config.onOpen();
}
};
ws.onclose = (error) => {
console.log("Remix dev asset server web socket closed. Reconnecting...");
setTimeout(
() =>
remixLiveReloadConnect({
onOpen: () => window.location.reload(),
}),
1000
);
ws.onclose = (event) => {
if (event.code === 1006) {
console.log("Remix dev asset server web socket closed. Reconnecting...");
setTimeout(
() =>
remixLiveReloadConnect({
onOpen: () => window.location.reload(),
}),
1000
);
}
};
ws.onerror = (error) => {
console.log("Remix dev asset server web socket error:");
Expand Down