Skip to content

Commit ba59e78

Browse files
committed
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 d5ebb37 commit ba59e78

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,
@@ -152,6 +155,7 @@ export default class LiveSocket {
152155
this.hooks = opts.hooks || {}
153156
this.uploaders = opts.uploaders || {}
154157
this.loaderTimeout = opts.loaderTimeout || LOADER_TIMEOUT
158+
this.disconnectedTimeout = opts.disconnectedTimeout || DISCONNECTED_TIMEOUT
155159
this.reloadWithJitterTimer = null
156160
this.maxReloads = opts.maxReloads || MAX_RELOADS
157161
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
@@ -155,6 +155,7 @@ export default class View {
155155
this.lastAckRef = null
156156
this.childJoins = 0
157157
this.loaderTimer = null
158+
this.disconnectedTimer = null
158159
this.pendingDiffs = []
159160
this.pendingForms = new Set()
160161
this.redirect = false
@@ -266,6 +267,7 @@ export default class View {
266267

267268
hideLoader(){
268269
clearTimeout(this.loaderTimer)
270+
clearTimeout(this.disconnectedTimer)
269271
this.setContainerClasses(PHX_CONNECTED_CLASS)
270272
this.execAll(this.binding("connected"))
271273
}
@@ -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
@@ -714,6 +714,7 @@ describe("View", function(){
714714

715715
afterEach(() => {
716716
HTMLFormElement.prototype.submit = submitBefore
717+
jest.useRealTimers()
717718
})
718719

719720
afterAll(() => {
@@ -774,6 +775,7 @@ describe("View", function(){
774775
})
775776

776777
test("displayError and hideLoader", done => {
778+
jest.useFakeTimers()
777779
let liveSocket = new LiveSocket("/live", Socket)
778780
let loader = document.createElement("span")
779781
let phxView = document.querySelector("[data-phx-session]")
@@ -789,15 +791,13 @@ describe("View", function(){
789791
expect(el.classList.contains("phx-error")).toBeTruthy()
790792
expect(el.classList.contains("phx-connected")).toBeFalsy()
791793
expect(el.classList.contains("user-implemented-class")).toBeTruthy()
792-
window.requestAnimationFrame(() => {
793-
expect(status.style.display).toBe("block")
794-
simulateVisibility(status)
795-
view.hideLoader()
796-
window.requestAnimationFrame(() => {
797-
expect(status.style.display).toBe("none")
798-
done()
799-
})
800-
})
794+
jest.runAllTimers()
795+
expect(status.style.display).toBe("block")
796+
simulateVisibility(status)
797+
view.hideLoader()
798+
jest.runAllTimers()
799+
expect(status.style.display).toBe("none")
800+
done()
801801
})
802802

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

0 commit comments

Comments
 (0)