# React SDK — Realtime

> Use the realtime client in React via useAerostack. Subscribe to channels, emit events, and track presence directly in your UI.

Access the realtime client via the `useAerostack` hook.

```tsx

const { realtime } = useAerostack()
```

The `realtime` object is a `RealtimeClient` instance — connected automatically when `AerostackProvider` mounts.

## RealtimeClient methods

### `realtime.channel(topic, options?)`

Get (or create) a subscription for a topic. Returns a `RealtimeSubscription`.

```ts
const channel = realtime.channel('orders')           // DB table subscription
const channel = realtime.channel('chat/general')     // Custom pub/sub channel
const channel = realtime.channel('ai-chat/session-1') // Session channel
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `topic` | `string` | Channel topic. No slash → DB table subscription. With slash → custom channel. |
| `options.filter` | `Record<string, any>` | Optional server-side filter |

---

### `realtime.onStatusChange(callback)`

Subscribe to connection status changes. Returns an unsubscribe function.

```ts
const unsubscribe = realtime.onStatusChange((status) => {
  // 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'disconnected'
  setConnectionStatus(status)
})

// Cleanup:
unsubscribe()
```

---

### `realtime.connect()` / `realtime.disconnect()`

Manually connect or disconnect. The SDK connects automatically on mount.

```ts
await realtime.connect()
realtime.disconnect()
```

---

### `realtime.setToken(newToken)`

Update the auth token on a live connection (e.g. after token refresh).

```ts
realtime.setToken(newAccessToken)
```

---

## RealtimeSubscription methods

Returned by `realtime.channel()`.

### `.on(event, callback)`

Register a callback for an event. Returns `this` (chainable).

```ts
channel.on('INSERT', ({ data }) => { ... })
channel.on('UPDATE', ({ data, old }) => { ... })
channel.on('DELETE', ({ data }) => { ... })
channel.on('*', (payload) => { ... })           // All events

// Custom named events (pub/sub):
channel.on('todo:created', ({ data }) => { ... })
channel.on('presence:join', ({ data }) => { ... })
```

**Payload shape:**

```ts
interface RealtimePayload {
  type: 'db_change' | 'event'
  topic: string
  operation?: 'INSERT' | 'UPDATE' | 'DELETE'  // DB changes
  event?: string                               // Custom event name
  data: T                                      // New/current value
  old?: T                                      // Previous value (UPDATE/DELETE)
  userId?: string
  timestamp?: number | string
}
```

---

### `.off(event, callback)`

Remove a specific event listener.

```ts
const handler = ({ data }) => setItems(prev => [...prev, data])
channel.on('item:added', handler)
// later:
channel.off('item:added', handler)
```

---

### `.subscribe()`

Send the subscribe message to the server. Call after registering all `.on()` listeners.

```ts
channel.on('INSERT', handler).subscribe()
```

---

### `.unsubscribe()`

Send unsubscribe and clear all listeners. Call on component cleanup.

```ts
return () => channel.unsubscribe()
```

---

### `.publish(event, data, options?)`

Broadcast a custom event to all channel subscribers.

```ts
channel.publish('todo:created', { id: '123', text: 'Buy milk' })
channel.publish('message', { text: 'Hello!' }, { persist: true })
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `event` | `string` | Event name |
| `data` | `any` | Payload to broadcast |
| `options.persist` | `boolean` | Store message in DB for history |

---

### `.track(state)`

Announce your presence on this channel.

```ts
channel.track({
  userId: user.id,
  name: user.name,
  color: '#FF5733',
  status: 'online',
})
```

---

### `.untrack()`

Remove your presence from this channel. Broadcasts `presence:leave`.

```ts
channel.untrack()
```

---

### `.getHistory(limit?, before?)`

Fetch persisted messages. Requires previous publishes with `{ persist: true }`.

```ts
const messages = await channel.getHistory(50)
// messages: HistoryMessage[]

// Paginate:
const older = await channel.getHistory(50, messages[messages.length - 1].created_at)
```

**`HistoryMessage` shape:**

```ts
interface HistoryMessage {
  id: string
  room_id: string
  user_id: string
  event: string
  data: any
  created_at: number
}
```

---

## React pattern: useEffect subscription

```tsx

function LiveData() {
  const { realtime } = useAerostack()
  const [items, setItems] = useState([])

  useEffect(() => {
    const channel = realtime.channel('items')

    channel
      .on('INSERT', ({ data }) => setItems(prev => [...prev, data]))
      .on('UPDATE', ({ data }) => setItems(prev => prev.map(i => i.id === data.id ? data : i)))
      .on('DELETE', ({ data }) => setItems(prev => prev.filter(i => i.id !== data.id)))
      .subscribe()

    return () => channel.unsubscribe()
  }, [realtime])

  return <ul>{items.map(i => <li key={i.id}>{i.name}</li>)}</ul>
}
```
