React Hooks

vuer-rtc provides React hooks for seamless integration with React applications.

See Operations for the complete reference of operation types you can use with these hooks.

Installation

The hooks are available from the /hooks subpath:

import {
  GraphProvider,
  useGraph,
  useNode,
  useNodeProperty,
  useRootKey,
  useGraphActions,
  useUndoRedoShortcuts
} from '@vuer-ai/vuer-rtc/hooks';

GraphProvider

Wrap your app with GraphProvider to provide the graph context:

function App() {
  return (
    <GraphProvider sessionId="my-session" onSend={sendToServer}>
      <Scene />
    </GraphProvider>
  );
}

Props

PropTypeDescription
sessionIdstringUnique session identifier
onSend(msg: CRDTMessage) => voidCallback when a message is ready to send

useGraph

Get the current graph state:

function Scene() {
  const graph = useGraph();
  return (
    <>
      {Object.keys(graph.nodes).map(key => (
        <Node key={key} nodeKey={key} />
      ))}
    </>
  );
}

useNode

Subscribe to a specific node by key. Re-renders only when that node changes:

function Node({ nodeKey }: { nodeKey: string }) {
  const node = useNode(nodeKey);

  if (!node) return null;

  return (
    <mesh position={node.position}>
      <boxGeometry />
    </mesh>
  );
}

useNodeProperty

Subscribe to a specific property of a node. More granular than useNode—only re-renders when that property changes:

function NodePosition({ nodeKey }: { nodeKey: string }) {
  const position = useNodeProperty<[number, number, number]>(nodeKey, 'position');

  if (!position) return null;

  return <mesh position={position}><boxGeometry /></mesh>;
}

useRootKey

Subscribe to the root node key. Re-renders only when the root key changes:

function SceneRoot() {
  const rootKey = useRootKey();

  if (!rootKey) return <p>No scene loaded</p>;

  return <Node nodeKey={rootKey} />;
}

useGraphActions

Get actions to modify the graph:

function Node({ nodeKey }: { nodeKey: string }) {
  const node = useNode(nodeKey);
  const { edit, commit, undo, redo } = useGraphActions();

  const onDrag = (delta: [number, number, number]) => {
    edit({ otype: 'vector3.add', key: nodeKey, path: 'position', value: delta });
  };

  const onDragEnd = () => {
    commit(`Move ${node?.name}`);
  };

  if (!node) return null;
  return <mesh position={node.position} onDrag={onDrag} onDragEnd={onDragEnd} />;
}

Available Actions

ActionSignatureDescription
edit(op: Operation) => voidAdd operation to edit buffer (uncommitted)
commit(description?: string) => voidCommit edits as a single message
cancelEdits() => voidDiscard uncommitted edits
undo() => voidUndo last committed message
redo() => voidRedo last undone message
receive(msg: CRDTMessage) => voidProcess incoming remote message

useUndoRedoShortcuts

Enables keyboard shortcuts for undo/redo:

  • Cmd/Ctrl+Z: Undo
  • Cmd/Ctrl+Shift+Z: Redo
function App() {
  useUndoRedoShortcuts();

  return <Scene />;
}

Full Example

import {
  GraphProvider,
  useGraph,
  useNode,
  useGraphActions,
  useUndoRedoShortcuts
} from '@vuer-ai/vuer-rtc/hooks';

function App() {
  const wsRef = useRef<WebSocket | null>(null);

  const handleSend = (msg: CRDTMessage) => {
    wsRef.current?.send(JSON.stringify(msg));
  };

  return (
    <GraphProvider sessionId="user-123" onSend={handleSend}>
      <SceneWithShortcuts />
    </GraphProvider>
  );
}

function SceneWithShortcuts() {
  useUndoRedoShortcuts(); // Cmd/Ctrl+Z to undo, Cmd/Ctrl+Shift+Z to redo

  return (
    <>
      <Canvas>
        <Scene />
      </Canvas>
      <UndoRedoButtons />
    </>
  );
}

function Scene() {
  const graph = useGraph();
  const { receive } = useGraphActions();

  // Handle incoming messages
  useEffect(() => {
    const ws = new WebSocket('wss://server/room');
    ws.onmessage = (e) => receive(JSON.parse(e.data));
    return () => ws.close();
  }, [receive]);

  return (
    <>
      {Object.keys(graph.nodes)
        .filter(k => !graph.nodes[k].deletedAt)
        .map(key => <Node key={key} nodeKey={key} />)}
    </>
  );
}

function UndoRedoButtons() {
  const { undo, redo } = useGraphActions();
  return (
    <div>
      <button onClick={undo}>Undo</button>
      <button onClick={redo}>Redo</button>
    </div>
  );
}

Next: See the Complete Integration Guide for end-to-end examples of building collaborative applications.