React, Next.js, Solid

There is no framework package. How to write your own hook around the client, and the two mistakes to avoid.

There is no framework package, on purpose. The client is a plain object, which is all a hook needs, and a wrapper per framework version would age faster than the SDK does. A hook in your own code is a few lines.

One client for the whole app

Create the Realtime instance once — a module constant or a context — never inside a component, or every render opens a socket.

realtime.ts
import * as Blackevin from '@blackevin/client';

export const realtime = new Blackevin.Realtime({ authUrl: '/api/blackevin-token' });

In Next.js this file is client-only: import it from a component marked 'use client', not from a server component. The API key never appears in it — authUrl points at a route on your own server, described in From a server.

A channel hook

use-channel.ts
import { useEffect, useRef } from 'react';
import { realtime } from './realtime';

export function useChannel(name: string, onMessage: (message: { name?: string; data?: unknown }) => void) {
  const handler = useRef(onMessage);

  handler.current = onMessage;

  useEffect(() => {
    let active = true;

    realtime.channels.get(name).subscribe(message => {
      if (active) handler.current(message);
    });

    return () => {
      active = false;
    };
  }, [name]);
}

Two details carry the weight:

  • The handler lives in a ref. The effect depends on the channel name alone, so a new callback on every render does not subscribe again.
  • The active flag is the cleanup. The client has no unsubscribe yet, so the effect cannot remove its listener — it silences it instead. The listener stays registered until the connection closes, which is harmless for a channel a screen uses for its whole life, and worth knowing for a list that mounts and unmounts hundreds of rows. For that shape, subscribe once higher up and pass the data down.
function Lobby() {
  const [messages, setMessages] = useState<unknown[]>([]);

  useChannel('chat:lobby', message => setMessages(all => [...all, message.data]));

  return <List items={messages} />;
}

Solid, Vue, Svelte

The same two rules in each framework's own words: one client per app, and a subscription made in the mount hook (onMount, onMounted) guarded by a flag flipped in the unmount hook.

On this page