Skip to content

useDataTable

Hook for schema-driven data tables with pagination, sorting, filtering, and selection.

Basic Usage

tsx
import { useDataTable } from '@anhanga/react'
import { Scope } from '@anhanga/core'

function PersonTable() {
  const table = useDataTable({
    schema: PersonSchema.provide(),
    scope: Scope.index,
    handlers: personHandlers,
    hooks: personHooks,
    component: componentContract,
    translate: t,
  })

  if (table.loading) return <Loading />
  if (table.empty) return <Empty />

  return (
    <div>
      <table>
        <thead>
          <tr>
            {table.columns.map((col) => (
              <th key={col.name} onClick={() => table.setSort(col.name)}>
                {t(`person.fields.${col.name}`)}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {table.rows.map((row) => (
            <tr key={table.getIdentity(row)}>
              {table.columns.map((col) => (
                <td key={col.name}>{table.formatValue(col.name, row[col.name], row)}</td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>

      <Pagination
        page={table.page}
        total={table.totalPages}
        onChange={table.setPage}
      />
    </div>
  )
}

Options

typescript
interface UseDataTableOptions {
  schema: SchemaProvide
  scope: ScopeValue
  handlers?: Record<string, HandlerFn>
  hooks?: SchemaHooks
  context?: Record<string, unknown>
  component: ComponentContract
  pageSize?: number
  translate?: TranslateContract
}
OptionRequiredDescription
schemaYesOutput of SchemaDefinition.provide()
scopeYesCurrent scope (typically Scope.index)
handlersNoAction handlers
hooksNoLifecycle hooks (fetch hooks provide data)
componentYesComponentContract implementation
pageSizeNoItems per page (default varies)
translateNoTranslation function

Return Value

Data

PropertyTypeDescription
rowsRecord<string, unknown>[]Current page data
loadingbooleanTrue while fetching data
emptybooleanTrue if no rows

Columns

PropertyTypeDescription
columnsResolvedColumn[]Visible columns
availableColumnsResolvedColumn[]All available columns
visibleColumnsstring[]Names of visible columns
toggleColumn(name)functionToggle column visibility

Pagination

PropertyTypeDescription
pagenumberCurrent page
limitnumberItems per page
totalnumberTotal record count
totalPagesnumberTotal page count
setPage(page)functionNavigate to page
setLimit(limit)functionChange page size

Sorting

PropertyTypeDescription
sortFieldstring | undefinedCurrent sort field
sortOrder'asc' | 'desc' | undefinedSort direction
setSort(field)functionToggle sort on field

Filtering

PropertyTypeDescription
filtersRecord<string, unknown>Active filters
setFilter(field, value)functionSet a filter value
clearFilters()functionClear all filters

Selection

PropertyTypeDescription
selectedRecord<string, unknown>[]Selected rows
isSelected(record)functionCheck if row is selected
toggleSelect(record)functionToggle row selection
selectAll()functionSelect all rows
clearSelection()functionClear selection

Actions

PropertyTypeDescription
actionsResolvedAction[]Top-level actions (e.g., "Add")
getRowActions(record)functionGet actions for a specific row

Utilities

MethodDescription
reload()Re-fetch current page data
formatValue(name, value, record)Format a cell value using column config
getIdentity(record)Get the identity key for a record

Column Configuration

Fields become table columns when marked with .column():

typescript
text().column()               // basic column
text().column().sortable()    // sortable
text().column().filterable()  // filterable

Fetch Hooks

Table data is loaded through fetch hooks:

typescript
const personHooks = PersonSchema.hooks({
  fetch: {
    [Scope.index]: async ({ params }) => {
      return personService.paginate(params)
    },
  },
})

The params object contains pagination, sorting, and filter state:

typescript
interface PaginateParams {
  page: number
  limit: number
  sort?: string
  order?: 'asc' | 'desc'
  filters?: Record<string, unknown>
}

The hook expects a PaginatedResult:

typescript
interface PaginatedResult<T> {
  data: T[]
  total: number
  page: number
  limit: number
}

ResolvedColumn

typescript
interface ResolvedColumn {
  name: string
  config: FieldConfig
  table: TableConfig
}

Released under the MIT License.