Overview
useTable is the React/React Native hook for a custom table’s rows, backed by RTK Query against the /db surface. It returns the current page of rows plus loading/refetch state, query controls, and row CRUD actions. The query knobs (page, limit, sort, filters, includeDeleted) are stored in the tables Redux slice keyed by table name, so multiple consumers of the same table share one view.
See Custom Tables — Overview for a guide, and Custom Tables for the feature concept.
Usage Example
Parameters
string
required
The logical custom-table name (without the
custom_ prefix). It may change between renders: the hook never shows the previous table’s rows under the new name. It loads the new table from scratch, or shows that table’s own rows straight away if they are still cached.Partial<TableViewState>
Optional initial view — any of
page, limit, sortBy, sortDir, filters, includeDeleted. Used to seed the stored view the first time this table is read.Return Values
T[]
The current page of rows.
PaginationMetadata | null
Pagination metadata (
page, pageSize, totalPages, totalItems, hasMore), or null before the first load.It describes the page currently on screen, which during a page change is still the previous one — the rows and their metadata stay in step.So a pager needs its target and its bound from view.page, the page you last asked for, not from pagination:setPage(pagination.page + 1)computes the same target twice if clicked again before the first request lands, so the second click appears to do nothing.pagination.hasMoreis likewise the previous page’s answer, so a second click can run off the end — asking for page 3 when page 2 was the last.
totalPages and totalItems are safe to read mid-change: they describe the whole result set rather than a page, so view.page < pagination.totalPages is a correct bound either way.boolean
true when there is no page to show and a request is in flight — the first load, and a tableName change. An empty table is a page, so it reports false with zero rows.Changing page, sort or filters keeps the previous rows on screen with loading: false while the new query runs, so the table does not blank out between pages. Changing tableName does blank it: another table’s rows under a new table’s name would be wrong data, not stale data. Returning to a table this hook has already loaded shows that table’s own rows again rather than blanking, for as long as they remain cached.useTableRow follows the same rule for the same reason — a rowId change reports loading: true rather than leaving the previous record on screen.unknown
The query error, if any.
() => void
Re-runs the current query.
TableViewState
The current stored view:
{ page, limit, sortBy, sortDir, filters, includeDeleted }.(view: Partial<TableViewState>) => void
Merge-updates the stored view.
(page: number) => void
Changes the current page.
(filters: DbFilter[]) => void
Replaces the filter clauses (and resets to page 1). Each clause is
{ column, operator, value }; operators are eq, ne, gt, gte, lt, lte, in, contains, like, isNull, AND-combined.(sortBy?: string, sortDir?: 'asc' | 'desc') => void
Sets the sort column/direction (and resets to page 1).
(includeDeleted: boolean) => void
On a paranoid table, toggles whether soft-deleted rows are included (resets to page 1).
(data: Record<string, unknown>) => Promise<T>
Inserts a row. Managed columns (
id/createdAt/updatedAt/deletedAt) are server-set and rejected from the body.(rowId: string, data: Record<string, unknown>) => Promise<T>
Updates a row;
updatedAt is bumped automatically on a timestamped table.(rowId: string, opts?: { force?: boolean }) => Promise<{ deleted: boolean; soft: boolean }>
Deletes a row. Soft-deletes on a paranoid table by default; pass
{ force: true } to hard-delete.(rowId: string) => Promise<T>
Clears
deletedAt on a soft-deleted row (paranoid tables only).
