DocsReferenceReact Hooks

React Hooks

The @topgunbuild/react package provides a set of hooks to make it easy to build reactive applications with TopGun.

Setup

Wrap your application in the TopGunProvider:
src/App.tsx
import { TopGunClient } from '@topgunbuild/client';
import { TopGunProvider } from '@topgunbuild/react';

const client = new TopGunClient({ ... });

function App() {
  return (
    <TopGunProvider client={client}>
      <YourComponents />
    </TopGunProvider>
  );
}

useQuery

Subscribes to a live query and returns the results. The component will automatically re-render when the data changes.
components/TodoList.tsx
import { useQuery } from '@topgunbuild/react';

function TodoList() {
  // QueryFilter options: where, predicate, sort, limit, offset
  const { data, loading, error } = useQuery('todos', {
    where: { completed: false },
    sort: { createdAt: 'desc' }
  });

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  // Each item in data has a _key property for the map key
  return (
    <ul>
      {data.map(todo => <li key={todo._key}>{todo.title}</li>)}
    </ul>
  );
}

useMap / useORMap

Get direct access to a Map CRDT instance. The component will re-render whenever the map changes.

Use useMap for Last-Write-Wins maps (simple key-value) and useORMap for Observed-Remove maps (multi-value/sets).

components/UserProfile.tsx
import { useMap, useORMap } from '@topgunbuild/react';

// Simple Key-Value Store
function UserProfile({ userId }) {
  const map = useMap('users');
  const user = map.get(userId);

  return (
    <div>
      <h1>{user?.name}</h1>
      <button onClick={() => map.set(userId, { ...user, active: true })}>
        Activate
      </button>
    </div>
  );
}

// Multi-Value Set (Tags, Categories)
function ProductTags({ productId }) {
  const tagsMap = useORMap('product_tags');
  const tags = tagsMap.get(productId) || []; // Returns array or empty

  return (
    <div>
      {tags.map(tag => (
        <span key={tag} onClick={() => tagsMap.remove(productId, tag)}>
          {tag}
        </span>
      ))}
      <button onClick={() => tagsMap.add(productId, 'new-tag')}>
        Add Tag
      </button>
    </div>
  );
}

useMutation

A helper hook for performing mutations on a map without subscribing to changes.
components/CreateTodo.tsx
import { useMutation } from '@topgunbuild/react';

function CreateTodo() {
  // Returns { create, update, remove, map }
  const { create, update, remove } = useMutation('todos');

  const handleCreate = (text) => {
    const id = crypto.randomUUID();
    create(id, { text, completed: false, createdAt: Date.now() });
  };

  const handleComplete = (id, todo) => {
    update(id, { ...todo, completed: true });
  };

  const handleDelete = (id) => {
    remove(id);
  };

  return <button onClick={() => handleCreate('New Task')}>Add</button>;
}

useTopic

Subscribe to a Pub/Sub topic for ephemeral messaging. Messages are not persisted.
components/ChatRoom.tsx
import { useTopic } from '@topgunbuild/react';

function ChatRoom({ roomId }) {
  const topic = useTopic(`room:${roomId}`, (msg, ctx) => {
    console.log('New message:', msg);
  });

  const sendMessage = (text) => {
    topic.publish({ text, sender: 'me' });
  };

  return <button onClick={() => sendMessage('Hello!')}>Send</button>;
}

useClient

Get direct access to the TopGunClient instance from context.
components/CustomComponent.tsx
import { useClient } from '@topgunbuild/react';

function CustomComponent() {
const client = useClient();

// Direct access to client methods
const lock = client.getLock('my-resource');

return <div>...</div>;
}