Skip to content

Commit 447db31

Browse files
fix(state.js): drop deprecated unload listener, use pagehide (#6195) (#6479)
Chrome 134+ emits a deprecation warning for window `unload` listeners. The connect handler in state.js registered three lifecycle listeners (pagehide, beforeunload, unload), with `unload` and `beforeunload` both calling the same disconnect routine. - Remove the deprecated `unload` add/remove listeners. - Update `pagehideHandler` to disconnect on every page-hide event (not only the bfcache `event.persisted` case), since `pagehide` is the canonical replacement for `unload` per the Web Page Lifecycle API and fires reliably on tab close, navigation, and bfcache. - Keep `beforeunload` as a non-deprecated synchronous disconnect fallback for browsers where pagehide ordering differs. Adds a unit regression test that asserts the template no longer registers a `unload` listener while retaining `pagehide` and `beforeunload`.
1 parent 70bd785 commit 447db31

2 files changed

Lines changed: 52 additions & 5 deletions

File tree

packages/reflex-base/src/reflex_base/.templates/web/utils/state.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -618,16 +618,21 @@ export const connect = async (
618618

619619
const disconnectTrigger = (event) => {
620620
if (socket.current?.connected) {
621-
console.log("Disconnect websocket on unload");
621+
console.log("Disconnect websocket on page navigation");
622622
socket.current.disconnect();
623623
}
624624
};
625625

626626
const pagehideHandler = (event) => {
627-
if (event.persisted && socket.current?.connected) {
627+
if (!socket.current?.connected) {
628+
return;
629+
}
630+
if (event.persisted) {
628631
console.log("Disconnect backend before bfcache on navigation");
629-
socket.current.disconnect();
632+
} else {
633+
console.log("Disconnect websocket on pagehide");
630634
}
635+
socket.current.disconnect();
631636
};
632637

633638
// Once the socket is open, hydrate the page.
@@ -636,7 +641,6 @@ export const connect = async (
636641
setConnectErrors([]);
637642
window.addEventListener("pagehide", pagehideHandler);
638643
window.addEventListener("beforeunload", disconnectTrigger);
639-
window.addEventListener("unload", disconnectTrigger);
640644
if (socket.current.rehydrate) {
641645
socket.current.rehydrate = false;
642646
queueEvents(initialEvents(), socket, true, navigate, params);
@@ -666,7 +670,6 @@ export const connect = async (
666670
socket.current.wait_connect = false;
667671
const try_reconnect =
668672
reason !== "io server disconnect" && reason !== "io client disconnect";
669-
window.removeEventListener("unload", disconnectTrigger);
670673
window.removeEventListener("beforeunload", disconnectTrigger);
671674
window.removeEventListener("pagehide", pagehideHandler);
672675
if (try_reconnect) {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""Regression tests for the state.js frontend template."""
2+
3+
from pathlib import Path
4+
5+
STATE_JS_TEMPLATE = (
6+
Path(__file__).parents[3]
7+
/ "packages/reflex-base/src/reflex_base/.templates/web/utils/state.js"
8+
)
9+
10+
11+
def test_state_js_does_not_register_deprecated_unload_listener() -> None:
12+
"""The template must not register the deprecated `unload` event listener.
13+
14+
Regression for https://github.com/reflex-dev/reflex/issues/6195: Chrome
15+
emits a deprecation warning when pages register `unload` handlers. The
16+
`pagehide` listener already covers the cases `unload` was being used for
17+
(tab close, navigation, bfcache), so the deprecated listener must not be
18+
re-introduced.
19+
"""
20+
content = STATE_JS_TEMPLATE.read_text()
21+
22+
assert 'addEventListener("unload"' not in content, (
23+
"state.js registers a deprecated `unload` listener; use `pagehide` instead."
24+
)
25+
assert 'removeEventListener("unload"' not in content, (
26+
"state.js still removes a `unload` listener that should no longer be registered."
27+
)
28+
29+
30+
def test_state_js_still_handles_page_lifecycle_disconnect() -> None:
31+
"""The template must still disconnect on page lifecycle events.
32+
33+
Replacing the deprecated `unload` listener with `pagehide` is the
34+
recommended Web Page Lifecycle pattern; verify the replacement listeners
35+
remain wired so the socket is closed when the user navigates away.
36+
"""
37+
content = STATE_JS_TEMPLATE.read_text()
38+
39+
assert 'addEventListener("pagehide"' in content, (
40+
"state.js should register a `pagehide` listener to disconnect the socket."
41+
)
42+
assert 'addEventListener("beforeunload"' in content, (
43+
"state.js should keep its `beforeunload` listener as a disconnect fallback."
44+
)

0 commit comments

Comments
 (0)