Skip to content

Handle Connection State Changes ​

Problem ​

You need to show connection status in your UI, disable controls while offline, or trigger logic when the client reconnects.

Solution ​

Subscribe to the connection state observable/stream and react to each state transition.

Code ​

typescript
import { StarfishClient } from "@starfish/sdk";

const client = new StarfishClient({
  server: "ws://localhost:4000",
  reconnect: { enabled: true },
});

client.connectionState$.subscribe((state) => {
  switch (state) {
    case "disconnected":
      showBanner("Disconnected");
      disableControls();
      break;
    case "connecting":
      showBanner("Connecting...");
      break;
    case "connected":
      hideBanner();
      enableControls();
      break;
    case "reconnecting":
      showBanner("Reconnecting...");
      disableControls();
      break;
  }
});

await client.connect();
python
from starfish import StarfishClient, StarfishClientOptions, ReconnectOptions, ConnectionState

client = StarfishClient(StarfishClientOptions(
    server="ws://localhost:4000",
    reconnect=ReconnectOptions(enabled=True),
))

def on_state_change(state: ConnectionState):
    if state == ConnectionState.DISCONNECTED:
        show_banner("Disconnected")
        disable_controls()
    elif state == ConnectionState.CONNECTING:
        show_banner("Connecting...")
    elif state == ConnectionState.CONNECTED:
        hide_banner()
        enable_controls()
    elif state == ConnectionState.RECONNECTING:
        show_banner("Reconnecting...")
        disable_controls()

client.connection_state.subscribe(on_state_change)

await client.connect()
swift
import StarfishClient

let client = StarfishClient(options: StarfishClientOptions(
    server: URL(string: "ws://localhost:4000")!,
    reconnect: ReconnectOptions(enabled: true)
))

Task {
    for await state in client.connectionState {
        switch state {
        case .disconnected:
            showBanner("Disconnected")
            disableControls()
        case .connecting:
            showBanner("Connecting...")
        case .connected:
            hideBanner()
            enableControls()
        case .reconnecting:
            showBanner("Reconnecting...")
            disableControls()
        }
    }
}

try await client.connect()

Explanation ​

The connection lifecycle has four states:

disconnected → connecting → connected
                    ↑            ↓
              reconnecting ←─────┘ (on connection loss)
  • disconnected — not connected and not trying. This is the initial state and the state after calling disconnect() or exhausting all retries.
  • connecting — establishing the initial connection.
  • connected — fully connected and ready to send/receive.
  • reconnecting — lost connection, attempting to reconnect. With reconnection enabled, the client will retry with exponential backoff.

The observable/stream fires on every transition, so you can update your UI or pause operations accordingly.

Variations ​

Read the current state synchronously ​

typescript
// Access the current value without subscribing
const currentState = client.connectionState$.value;
if (currentState === "connected") {
  client.publish("chat", { text: "I'm online!" });
}
python
# Access the current value without subscribing
current_state = client.connection_state.value
if current_state == ConnectionState.CONNECTED:
    await client.publish("chat", {"text": "I'm online!"})
swift
// Use the observable's current value
// Note: In Swift, check the stream for the latest state
// or maintain your own @Published property from the stream

Guard actions behind connection state ​

typescript
function safeSend(client, to, payload) {
  if (client.connectionState$.value !== "connected") {
    console.warn("Not connected — message not sent");
    return;
  }
  client.send(to, payload);
}
python
async def safe_send(client, to, payload):
    if client.connection_state.value != ConnectionState.CONNECTED:
        print("Not connected — message not sent")
        return
    await client.send(to, payload)
swift
func safeSend(_ client: StarfishClient, to: String, payload: AnyCodable) {
    do {
        try client.send(to: to, payload: payload)
    } catch {
        print("Not connected — message not sent")
    }
}