Skip to content

fix: connection status indicator stuck at idle after component remount#2104

Draft
Copilot wants to merge 2 commits intomainfrom
copilot/investigate-idle-connection-status
Draft

fix: connection status indicator stuck at idle after component remount#2104
Copilot wants to merge 2 commits intomainfrom
copilot/investigate-idle-connection-status

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 22, 2026

After extended usage, the connection indicator would permanently show idle despite the WebSocket remaining fully functional. Root cause was a window-singleton behaviour in @apollo/client-react-streaming.

Root Cause

ApolloNextAppProvider stores the Apollo Client (and its embedded graphql-ws client) as window[Symbol.for('ApolloClientSingleton')] ??= makeClient() — called once per browser session. On any ApolloClientProvider remount (locale change, error-boundary recovery, etc.), the new component instance starts with fresh useState('idle'), but the graphql-ws on:{} handlers still hold a closure over the first mount's setState. React silently drops setState calls on unmounted components, so the new instance's state is never updated — stuck at 'idle' forever while subscriptions continue working normally.

Changes

  • Module-level forwarding callbacks (wsConnectionStateCallback, wsErrorCallback): graphql-ws event handlers now call through mutable module-level refs instead of directly closing over a per-instance setState.

  • useEffect (empty deps) re-wires on every mount: points the module-level callbacks at the current component's setState; resets to no-ops on unmount so stale events don't touch an unmounted component.

  • useState lazy initialisers read lastKnownConnectionState / lastKnownError (updated before each callback invocation) so a remounting component immediately reflects the correct state without waiting for the next WS event.

  • globalWsClient + module-level wsIsResettingRef: ensures resetConnection() and the closed-event guard work correctly after a remount where the per-instance React refs are null.

// Before — captured setState_1 from first mount; dead after remount
on: { connected: () => setConnectionState('connected') }

// After — always dispatches to the currently mounted component
on: { connected: () => wsConnectionStateCallback('connected') }
// ...
useEffect(() => {
  wsConnectionStateCallback = (s) => setState(s); // re-wired on every mount
  return () => { wsConnectionStateCallback = () => {}; };
}, []);

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Screenshots

No UI changes — only the connection indicator's correctness is affected.

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • My changes generate no new warnings

Root cause: @apollo/client-react-streaming stores the Apollo Client as a
window singleton (window[Symbol.for('ApolloClientSingleton')]). makeClient()
is only called once per browser session. On ApolloClientProvider remount the
graphql-ws event handlers still reference the old setState (from the first
mount), so the new component's state is never updated and the indicator shows
'idle' forever.

Fix:
- Route all graphql-ws on:{} callbacks through module-level mutable refs
  (wsConnectionStateCallback / wsErrorCallback) that are re-pointed to the
  current component's setState in a useEffect on every mount/remount.
- Use useState lazy initialisers reading lastKnownConnectionState /
  lastKnownError so a remounting component immediately shows the correct
  indicator value.
- Store globalWsClient at module level so resetConnection() works correctly
  even after remounts when the per-instance wsClientRef is null.
- Replace the per-instance isResettingRef with a module-level wsIsResettingRef
  so it persists correctly alongside the singleton WS client.

Agent-Logs-Url: https://github.com/FIRSTIsrael/lems/sessions/24c16f1d-7f5e-4fcd-b13d-a9558eeb631f

Co-authored-by: johnmeshulam <55348702+johnmeshulam@users.noreply.github.com>
Copilot AI changed the title [WIP] Investigate why connection status changes to idle randomly fix: connection status indicator stuck at idle after component remount Apr 22, 2026
Copilot AI requested a review from johnmeshulam April 22, 2026 20:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

Connection status changes to idle randomly

2 participants