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>;
}