pgTable/API Reference/tableApi
API ReferenceImperative surface

tableApi

The handle returned by usePgTable. Read rows and state, mutate them, query dirtiness, drop the entry — plus reactive useWatchData / useWatchState methods that are pre-bound to this table and pre-typed against its columns and state shape. Imperative calls hit the store directly; reactive calls subscribe through useSyncExternalStore. Forward tableApi through a table-hook or pass it as a prop — the inner component reads slices via tableApi.useWatchData() / tableApi.useWatchState(key), no id threading or generic restatement required.

Full surface

Reads, writes, dirty queries, lifecycle — flat, no chaining. Each method either reads the committed store or commits a new value and triggers per-slice notifications.

TSsurface.ts
// All methods on PgTableApi<C, S>. The second generic accepts the full
// state definition (the return of `pgt.tableState(...)`) — i.e. `typeof state`,
// not `typeof state.stateShape`. The shape is extracted internally.
type PgTableApi<C, S> = {
pgTableId: string

// Imperative reads — no React lifecycle, no memoisation.
getData():               PgTableData<C>
getColumns():            C
getColumnsArray():       PgTableColumnsArray<C>
getState():              PgTableStateValues<S['stateShape']>
getState<K>(key: K):     PgTableStateValues<S['stateShape']>[K]
lastUpdatedAt():         number | null

// Reactive reads — subscribe in render bodies, re-render on slice change.
// Pre-bound to pgTableId, pre-typed against C / S['stateShape']. No id arg,
// no generic restatement at the call site.
useWatchData():            PgTableData<C>
useWatchState():           PgTableStateValues<S['stateShape']>
useWatchState<K>(key: K):  PgTableStateValues<S['stateShape']>[K]

// Dirty.
isDirtyData():           boolean
isDirtyState():          boolean
dirtyStateProperties():  Partial<PgTableStateValues<S['stateShape']>>

// Writes — value or immer-style updater.
setData(next):    void
appendRows(rows): void
clearData():      void
resetData():      void

setState(next):   void
resetState():     void

// Lifecycle.
remove():         void
}

Reads

Two read modes, both keyed off the same tableApi. Imperative reads (getData, getState(key)) are a fresh look at the store on every call — use them in event handlers, effect bodies, and listener bodies. Reactive reads (useWatchData(), useWatchState(key)) subscribe and re-render the calling component on every committed change to that slice — use them in render bodies, never in handlers. The use* prefix is the signal; the get* prefix is the signal — same rule as anywhere else in React.

TScharacters-table.tsx
// Imperative reads — committed values, no subscription. Safe in handlers,
// effects, and listener bodies; useless in render bodies (no re-render).
const rows         = tableApi.getData()           // PgTableData<C>
const columns      = tableApi.getColumns()        // raw declaration object
const columnsArr   = tableApi.getColumnsArray()   // [{ columnName, columnInfo }, ...]
const allState     = tableApi.getState()          // every key
const onlySort     = tableApi.getState('sort')    // one key, narrowly typed
const lastUpdate   = tableApi.lastUpdatedAt()     // ms epoch | null

// Reactive reads — subscribe in render bodies, re-render on slice change.
// Already typed against this table's columns and state — no generic restatement.
const liveRows     = tableApi.useWatchData()      // PgTableData<C>
const liveSort     = tableApi.useWatchState('sort')  // PgTableStateValues<S['stateShape']>['sort']
const liveState    = tableApi.useWatchState()        // full state object; renders on any change

Writes

setData and setState both accept either a value or an updater function. Updaters get an immer draft — they can either return a new value or mutate the draft in place; pgTable accepts either. Every commit fires exactly the state-listeners affected by the changed keys.

TScharacters-table.tsx
// setData — pass a value, an updater that returns a new array, or an
// updater that mutates the immer draft.
tableApi.setData(newRows)
tableApi.setData((rows) => rows.filter((r) => !banned.has(r.id)))   // returns
tableApi.setData((rows) => { rows.push(newRow) })                   // mutates

// appendRows — sugar for "extend the current array".
tableApi.appendRows([row1, row2])

// clearData — write the empty array. Still counts as a dirty write.
tableApi.clearData()

// resetData — write the captured initialData back.
tableApi.resetData()

// setState — Partial value OR updater (return new value or mutate draft).
tableApi.setState({ page: 1 })
tableApi.setState((s) => { s.pagination.page = 1 })

// resetState — write initialState back across every key.
tableApi.resetState()

The updater context

The second arg to a setData / setState updater exposes committed reads — useful when the new value depends on the other slice. Reads from ctx always reflect the committed store, not the in-flight draft.

TScharacters-table.tsx
// The updater's 2nd arg gives committed reads (data/state/columns) —
// useful when one write depends on the other slice's value.

tableApi.setData((rows, ctx) => {
const selected = ctx.getState('selectedIds')
for (const r of rows) if (selected.includes(r.id)) r.starred = true
})

tableApi.setState((s, ctx) => {
// Recompute total from committed rows + draft pageSize.
s.pagination.total = ctx.getData().length
})

Dirty

isDirtyData / isDirtyState diff the current values against the captured initials. dirtyStateProperties returns the subset of state keys that differ — the convenient payload for a save endpoint that only takes changed fields.

TScharacters-table.tsx
// Dirtiness is calculated on every call — the store does not maintain a
// "dirty" flag. Each method diffs against the captured initials.

if (tableApi.isDirtyData() || tableApi.isDirtyState()) {
await api.save({
  rows:    tableApi.getData(),
  changes: tableApi.dirtyStateProperties(),   // only keys that differ from initial
})
}

Lifecycle

remove() drops the in-memory entry only. Persisted entries on webstorage are separate; reach for dropTableEverywhere() from the global-helpers when you want both gone.

TScharacters-table.tsx
// remove() drops the in-memory store entry. Persisted webstorage is
// untouched — use dropTableEverywhere() if you want both gone.
tableApi.remove()