Skip to content

Commit e7a2594

Browse files
authored
delay phx-disconnected to prevent reconnect flash (#3680)
* delay phx-disconnected to prevent reconnect flash See https://elixirforum.com/t/delaying-liveview-js-commands-avoid-quick-flash-of-trying-to-reconnect/64035. Relates to: phoenixframework/phoenix#5735 Maybe we need to tweak the timeout or make it configurable? * make disconnectedTimeout configurable
1 parent 24c3d9d commit e7a2594

File tree

4 files changed

+23
-10
lines changed

4 files changed

+23
-10
lines changed

assets/js/phoenix_live_view/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export const PHX_RELOAD_STATUS = "__phoenix_reload_status__"
6767
export const LOADER_TIMEOUT = 1
6868
export const MAX_CHILD_JOIN_ATTEMPTS = 3
6969
export const BEFORE_UNLOAD_LOADER_TIMEOUT = 200
70+
export const DISCONNECTED_TIMEOUT = 500
7071
export const BINDING_PREFIX = "phx-"
7172
export const PUSH_TIMEOUT = 30000
7273
export const LINK_HEADER = "x-requested-with"

assets/js/phoenix_live_view/live_socket.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
* @param {Object} [opts.uploaders] - The optional object for referencing LiveView uploader callbacks.
2929
* @param {integer} [opts.loaderTimeout] - The optional delay in milliseconds to wait before apply
3030
* loading states.
31+
* @param {integer} [opts.disconnectedTimeout] - The delay in milliseconds to wait before
32+
* executing phx-disconnected commands. Defaults to 500.
3133
* @param {integer} [opts.maxReloads] - The maximum reloads before entering failsafe mode.
3234
* @param {integer} [opts.reloadJitterMin] - The minimum time between normal reload attempts.
3335
* @param {integer} [opts.reloadJitterMax] - The maximum time between normal reload attempts.
@@ -78,6 +80,7 @@ import {
7880
DEFAULTS,
7981
FAILSAFE_JITTER,
8082
LOADER_TIMEOUT,
83+
DISCONNECTED_TIMEOUT,
8184
MAX_RELOADS,
8285
PHX_DEBOUNCE,
8386
PHX_DROP_TARGET,
@@ -151,6 +154,7 @@ export default class LiveSocket {
151154
this.hooks = opts.hooks || {}
152155
this.uploaders = opts.uploaders || {}
153156
this.loaderTimeout = opts.loaderTimeout || LOADER_TIMEOUT
157+
this.disconnectedTimeout = opts.disconnectedTimeout || DISCONNECTED_TIMEOUT
154158
this.reloadWithJitterTimer = null
155159
this.maxReloads = opts.maxReloads || MAX_RELOADS
156160
this.reloadJitterMin = opts.reloadJitterMin || RELOAD_JITTER_MIN

assets/js/phoenix_live_view/view.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ export default class View {
157157
this.lastAckRef = null
158158
this.childJoins = 0
159159
this.loaderTimer = null
160+
this.disconnectedTimer = null
160161
this.pendingDiffs = []
161162
this.pendingForms = new Set()
162163
this.redirect = false
@@ -268,6 +269,7 @@ export default class View {
268269

269270
hideLoader(){
270271
clearTimeout(this.loaderTimer)
272+
clearTimeout(this.disconnectedTimer)
271273
this.setContainerClasses(PHX_CONNECTED_CLASS)
272274
this.execAll(this.binding("connected"))
273275
}
@@ -909,7 +911,13 @@ export default class View {
909911
if(this.isMain()){ DOM.dispatchEvent(window, "phx:page-loading-start", {detail: {to: this.href, kind: "error"}}) }
910912
this.showLoader()
911913
this.setContainerClasses(...classes)
912-
this.execAll(this.binding("disconnected"))
914+
this.delayedDisconnected()
915+
}
916+
917+
delayedDisconnected(){
918+
this.disconnectedTimer = setTimeout(() => {
919+
this.execAll(this.binding("disconnected"))
920+
}, this.liveSocket.disconnectedTimeout)
913921
}
914922

915923
wrapPush(callerPush, receives){

assets/test/view_test.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,7 @@ describe("View", function(){
636636

637637
afterEach(() => {
638638
HTMLFormElement.prototype.submit = submitBefore
639+
jest.useRealTimers()
639640
})
640641

641642
afterAll(() => {
@@ -696,6 +697,7 @@ describe("View", function(){
696697
})
697698

698699
test("displayError and hideLoader", done => {
700+
jest.useFakeTimers()
699701
let liveSocket = new LiveSocket("/live", Socket)
700702
let loader = document.createElement("span")
701703
let phxView = document.querySelector("[data-phx-session]")
@@ -711,15 +713,13 @@ describe("View", function(){
711713
expect(el.classList.contains("phx-error")).toBeTruthy()
712714
expect(el.classList.contains("phx-connected")).toBeFalsy()
713715
expect(el.classList.contains("user-implemented-class")).toBeTruthy()
714-
window.requestAnimationFrame(() => {
715-
expect(status.style.display).toBe("block")
716-
simulateVisibility(status)
717-
view.hideLoader()
718-
window.requestAnimationFrame(() => {
719-
expect(status.style.display).toBe("none")
720-
done()
721-
})
722-
})
716+
jest.runAllTimers()
717+
expect(status.style.display).toBe("block")
718+
simulateVisibility(status)
719+
view.hideLoader()
720+
jest.runAllTimers()
721+
expect(status.style.display).toBe("none")
722+
done()
723723
})
724724

725725
test("join", async () => {

0 commit comments

Comments
 (0)