From 07e03effbfbe98ea03c2bf65711af6e1c35fdc52 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Tue, 16 Sep 2025 10:57:36 -0700 Subject: [PATCH 1/3] Reassociate sid and token when connecting websocket If there are active background tasks for a given client_token, reassociate the token and sid in the state when reconnecting so that updates from the background task go to to the new websocket. --- reflex/app.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/reflex/app.py b/reflex/app.py index 740aefa226d..8792188fefd 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -2189,3 +2189,10 @@ async def link_token_to_sid(self, sid: str, token: str): if new_token: # Duplicate detected, emit new token to client await self.emit("new_token", new_token, to=sid) + + # Update client state to apply new sid/token for running background tasks. + async with self.app.modify_state( + _substate_key(new_token or token, self.app.state_manager.state) + ) as state: + state.router_data[constants.RouteVar.SESSION_ID] = sid + state.router = RouterData.from_router_data(state.router_data) From 82d80cd63595d487658cf48842354a791fd730b0 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Wed, 17 Sep 2025 10:36:12 -0700 Subject: [PATCH 2/3] Do not queue websocket events until the websocket is connected --- reflex/.templates/web/utils/state.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/reflex/.templates/web/utils/state.js b/reflex/.templates/web/utils/state.js index a63fa4f09c8..e77c5a4950c 100644 --- a/reflex/.templates/web/utils/state.js +++ b/reflex/.templates/web/utils/state.js @@ -478,8 +478,8 @@ export const queueEvents = async ( * @param params The params object from React Router */ export const processEvent = async (socket, navigate, params) => { - // Only proceed if the socket is up and no event in the queue uses state, otherwise we throw the event into the void - if (!socket && isStateful()) { + // Only proceed if the socket is up or no event in the queue uses state, otherwise we throw the event into the void + if (isStateful() && !(socket && socket.connected)) { return; } @@ -576,11 +576,13 @@ export const connect = async ( }; // Once the socket is open, hydrate the page. - socket.current.on("connect", () => { + socket.current.on("connect", async () => { setConnectErrors([]); window.addEventListener("pagehide", pagehideHandler); window.addEventListener("beforeunload", disconnectTrigger); window.addEventListener("unload", disconnectTrigger); + // Drain any initial events from the queue. + await processEvent(socket.current, navigate, () => params.current); }); socket.current.on("connect_error", (error) => { @@ -893,7 +895,7 @@ export const useEventLoop = ( // Main event loop. useEffect(() => { // Skip if the backend is disabled - if (isBackendDisabled()) { + if (isBackendDisabled() || !socket.current || !socket.current.connected) { return; } (async () => { From 80655b97547137359779edf5b04dd6d4bb479c7f Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Wed, 17 Sep 2025 12:06:16 -0700 Subject: [PATCH 3/3] fully drain the queue on socket connect event --- reflex/.templates/web/utils/state.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/reflex/.templates/web/utils/state.js b/reflex/.templates/web/utils/state.js index e77c5a4950c..290628eb745 100644 --- a/reflex/.templates/web/utils/state.js +++ b/reflex/.templates/web/utils/state.js @@ -582,7 +582,9 @@ export const connect = async ( window.addEventListener("beforeunload", disconnectTrigger); window.addEventListener("unload", disconnectTrigger); // Drain any initial events from the queue. - await processEvent(socket.current, navigate, () => params.current); + while (event_queue.length > 0 && !event_processing) { + await processEvent(socket.current, navigate, () => params.current); + } }); socket.current.on("connect_error", (error) => {