gridcn1.2.2

Broadcasting your selection

Send your own selection to peers over a real websocket, and choose the divergence-proof payload form.

Follow-up to Multiplayer presence. The receiving side is the useDataGridPresence add-on. This page covers the other direction: broadcasting your own selection over a real websocket.

Wiring a real websocket

The demo on the Multiplayer presence page simulates 3 users with setInterval. A real integration looks like this:

"use client";

import { useEffect } from "react";
import { useDataGridPresence } from "@/components/data-grid-presence/data-grid-presence";

function usePresenceSync(roomId: string) {
  const { plugin, setPresenceHighlights } = useDataGridPresence();

  useEffect(() => {
    const socket = new WebSocket(`wss://your-server/rooms/${roomId}`);
    socket.onmessage = (event) => {
      const highlights = JSON.parse(event.data);
      setPresenceHighlights(highlights);
    };
    return () => socket.close();
  }, [setPresenceHighlights, roomId]);

  return plugin;
}

Sending your selection

Your own selection still needs to be broadcast in the other direction. Wire onSelectionChange on DataGridProvider rather than hand-rolling a useDataGridSelection() plus useEffect subscription. It already fires on every committed change (click, extend step, row, column, or all selection, and clear) from the actions layer of the store. It also fires once per step of a drag-extend gesture, exactly the granularity that a live broadcast wants.

The payload is the stable SelectionState: range corners and rangeStack are { rowId, columnId } pairs, and rowIds/columnIds are stable ids. It carries no view positions, so a peer with a different local sort or filter resolves every ref against its own view. Send next as-is (no hooks, no column-indexing by hand):

<DataGridProvider
  data={data}
  columns={columns}
  getRowId={getRowId}
  plugins={[plugin]}
  onSelectionChange={(next) => {
    if (!next.range && next.rowIds.length === 0 && next.columnIds.length === 0) {
      // cleared — send the leave message instead
      return;
    }
    socket.send(JSON.stringify({ id: userId, color, selection: next }));
  }}
>

The receiver applies the payload with actions.setSelection(next) or a controlled selection prop. Rows the receiver has filtered out and hidden or unknown columns are dropped, and dev mode warns once for the dropped refs (no mapping code on the receive side). A payload from a fuller view therefore applies cleanly to a narrower one.

On this page