Skip to content

Typescript sdk: SpacetimeDBProvider does not clean itself up and reloads/exiting doesn't disconnect properly #3365

@Lethalchip

Description

@Lethalchip

User Strike72 on discord encountered an issue where his react client doesn't correctly trigger their ClientDisconnected reducer: https://discord.com/channels/1037340874172014652/1424993383302041612

I guessed that this user was using the new react-hooks and took a look at the SpacetimeDBProvider. 🕵️

Right now the SpacetimeDBProvider builds the connection immediately, but never tears it down, so when the user refreshes or navigates away, the websocket stays open until it dies from inactivity or the browser closes. This leads to users not properly disconnecting from SpacetimeDB and frustration.

To clean up properly, the connection should be built inside a useEffect inside the provider itself, and call disconnect() in the cleanup function. Here is a drop-in replacement of the SpacetimeDBProvider.ts file that does exactly this:

import React from 'react';
import {
  DbConnectionBuilder,
  type DbConnectionImpl,
} from '../sdk/db_connection_impl';
import { SpacetimeDBContext } from './useSpacetimeDB';

export interface SpacetimeDBProviderProps<
  DbConnection extends DbConnectionImpl,
  ErrorContext,
  SubscriptionEventContext,
> {
  connectionBuilder: DbConnectionBuilder<
    DbConnection,
    ErrorContext,
    SubscriptionEventContext
  >;
  children?: React.ReactNode;
}

export function SpacetimeDBProvider<
  DbConnection extends DbConnectionImpl,
  ErrorContext,
  SubscriptionEventContext,
>({
  connectionBuilder,
  children,
}: SpacetimeDBProviderProps<
  DbConnection,
  ErrorContext,
  SubscriptionEventContext
>): React.JSX.Element {
  const [connection, setConnection] = React.useState<DbConnection | null>(null);

  React.useEffect(() => {
    const conn = connectionBuilder.build();
    setConnection(conn);

    return () => {
      try {
        conn.disconnect();
      } catch (err) {
        console.warn('[SpacetimeDBProvider] Error during disconnect:', err);
      }
    };
  }, [connectionBuilder]);

  if (!connection) return React.createElement(React.Fragment, null);

  return React.createElement(
    SpacetimeDBContext.Provider,
    { value: connection },
    children
  );
}

We're building the connection lazily inside the useEffect, and we're .disconnect()ing on unmount to automatically clean up the websocket. While the connection is initializing, we're also dropping in a placeholder React.Fragment.

I've confirmed this fix in my react-hooks test repo, although the above change won't be included since it's a modification of the npm package.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions