pgTable/API Reference/usePgTable
API ReferenceHook

usePgTable

The mount hook. Creates the store entry, wires state-listeners and persistence, and hands back a typed tableApi (carrying reactive useWatchData / useWatchState methods pre-bound to this table) plus the same hooks as destructurable bindings on the return. The single React-side ingress for a pgTable.

Signature

Three positional args — columns, state, and an options object. The options object covers identification, initial overrides, listeners, and persistence.

TSsignature.ts
// signature
usePgTable(columns, state, options?): {
tableApi:       PgTableApi<C, TDef>            // imperative API; carries scoped useWatch* too
useWatchData:   () => PgTableData<C>           // scoped, pre-typed, stable across renders
useWatchState:  ScopedUseWatchState<TDef>      // scoped, pre-typed, stable across renders
}

type PgTableOptions<TDef, C> = {
pgTableId?:      string                                          // caller-assigned id; auto-generated if omitted
state?:          Partial<PgTableStateValues<TDef['stateShape']>> // initial overrides, merged shallowly
data?:           PgTableData<C>                                  // initial rows
stateListeners?: { [K in keyof TDef['stateShape']]?: PgTableStateListener<C, TDef['stateShape']> }
onStateChange?:  (info: StateChangeInfo<TDef['stateShape'], C>) => void
persist?:        PgTablePersistConfig
}

Simple-table usage

The simple-table tier puts the table-config inline in a single <name>-table.tsx. The component is the composition-component: it owns usePgTable, subscribes via watch hooks, and renders.

TSXcharacters-table.tsx
import z from 'zod'
import { pgt, usePgTable } from '@westopp/pgtable'

const charactersColumns = pgt.tableColumns()({
id:   { type: z.string() },
name: { type: z.string() },
})

const charactersState = pgt.tableState(
{ sort: z.object({ by: z.string().nullable(), direction: z.enum(['asc', 'desc']) }) },
{ sort: { by: 'name', direction: 'asc' } },
)

export const CharactersTable = () => {
const { tableApi, useWatchData, useWatchState } = usePgTable(charactersColumns, charactersState, { data: [] })
const data = useWatchData()                  // pre-typed against charactersColumns
const sort = useWatchState('sort')           // pre-typed against charactersState
return <div>{data.length} rows, sorted by {sort.by ?? 'nothing'}</div>
}

Per-mount overrides

state overrides are merged shallowly over the declaration's initialState — useful for screen-specific defaults like a different pageSize. data is the seed for the rows; it is captured once on first render and never re-read.

TSXcharacters-table.tsx
usePgTable(charactersColumns, charactersState, {
pgTableId: 'characters',          // stable id — survives unmount when persisting,
                                  // and is the id passed to getTable() outside React
state:     { pageSize: 10 },      // per-instance default
data:      seed,                  // initial rows (captured once on mount)
})

State-listeners and onStateChange

Per-key stateListeners fire after a commit that touched that key, with three args: (newValue, prevState, currentData) — the new slice value, the full state object as it stood before the commit, and a getter for the live rows. To write back, capture tableApi from the destructure and call tableApi.setData / tableApi.setState from the listener body.

onStateChange fires once per commit and receives { state, prevState, propertiesUpdated, currentData } — the full new state, the full prev state, the list of keys that changed, and the same data getter.

TSXcharacters-table.tsx
// Trivial listeners stay inline — a one-line side effect, an obvious reset.
// Anything with branching, multi-key updates, or fetching
// moves to a named-table-state-listener — see Examples → Complex table.
const { tableApi } = usePgTable(charactersColumns, charactersState, {
pgTableId: 'characters',
stateListeners: {
  sort: (next, _prevState, _currentData) => {
    tableApi.setState((prev) => { prev.pagination.page = 1 })
  },
},
onStateChange: ({ propertiesUpdated, state, prevState }) => {
  analytics.track('table.state.change', { propertiesUpdated, state, prevState })
},
})

Initial values are captured once

initialState, initialData, and the resolved persist config are snapshotted at the first render of a given pgTableId. Subsequent renders that pass different values do not re-baseline the store — usePgTable is mount-time configuration.

TScapture.ts
// initialState, initialData, and the resolved persist config are captured
// ONCE on the first render. Mutating these objects on later renders has no
// effect — they are frozen into the store entry at create time.
//
// To replace rows after mount, call tableApi.setData(newRows).
// To re-baseline dirtiness, see Guides → Dirty tracking and save (Rebasing).