Capítulo 39 de 54

Chapter 39: Aggregation (React) Guide

Core Idea

rowAggregationFeature is independent of grouping — it works over any row subset (grand totals, filtered totals, a slice you pass in) via column.getAggregationValue(), and only needs columnGroupingFeature + a grouped row model when you specifically want per-group aggregate values on synthetic grouped rows.

Key Concepts

  • Register only named functions: aggregationFns: { sum: aggregationFn_sum, ... } — a definition passed directly to a column's aggregationFn needs no registry entry at all. stockFeatures includes rowAggregationFeature but not the named definitions — you still register those explicitly for string references to resolve.
  • Column config: aggregationFn accepts one value or an array. One value → scalar result. An array (strings and/or { id, aggregationFn } descriptors for a stable custom key) → an object keyed by name/id. Duplicate or missing descriptor ids and unregistered names warn in dev and leave that key undefined rather than throwing.
  • Grand totals / arbitrary subsets, no grouping required: column.getAggregationValue() with no args aggregates the default pre-grouped row model (filtering applied; grouping/sorting/expansion/pagination don't affect it) — cached against row model + depth + registry + column option. Pass { rows, maxDepth } to aggregate any other row set explicitly (e.g. table.getFilteredSelectedRowModel().rows, a manual .slice()) — explicit calls always recompute, never cached.
  • maxDepth semantics: relative to the supplied row array — 0 = those roots, 1 = their direct children, etc. A branch shallower than maxDepth contributes its deepest available row (a "unique frontier," never skipped entirely); Infinity selects terminal leaf rows. Configure a default via the column's maxAggregationDepth (defaults 0) for the cached no-args call, or override per explicit call. table.getMaxSubRowDepth() gives the deepest structural depth in the core row model — useful to compute "one level above the deepest leaf" (getMaxSubRowDepth() - 1).
  • Grouped aggregation = two independent features composed: register rowAggregationFeature and columnGroupingFeature + groupedRowModel: createGroupedRowModel(), then set aggregationFn on the columns that should show group totals. Render group-level values via the aggregatedCell column option (checked with cell.getIsAggregated(), which only exists once rowAggregationFeature is registered — a grouping-only table has no such API). Footers render normally through the adapter's usual footer renderer regardless.
  • Custom aggregation definitions via constructAggregationFn({ aggregate, merge? }): aggregate({ rows, getValue, column, columnId, maxDepth, table, groupingRow?, subRows? })rows is the depth-selected unique frontier; groupingRow/subRows only exist during grouped aggregation (root/explicit-row calls omit them). subRows are the group's immediate structural children (data rows at the terminal grouping level, synthetic sub-groups at nested levels) — use them instead of rows when a custom aggregation specifically wants immediate children rather than the depth-selected set.
  • merge for efficient nested aggregation: when provided, nested grouping combines already-computed sub-row results (subRowResults[i] = the result previously computed for subRows[i]) instead of re-running aggregate over the full flattened set at every level — worth adding for anything more expensive than a simple reduce.
  • Server/external values: a column's getAggregationValue({ rows }) => {...} can intercept the request — return { value } (including { value: undefined }) to mark it handled, or undefined to fall through to local computation. Put the same provider on defaultColumn to share it. manualAggregation: true disables the local fallback entirely for column.getAggregationValue() — independent of manualGrouping, which only controls whether the grouped row model itself runs.
  • 10 built-in definitions: sum (non-numbers contribute 0), count, min/max (numeric or Date), extent ([min, max], empty input → [undefined, undefined]), mean (numeric/number-like non-null), median (requires every value be a number), unique/uniqueCount (Set semantics), first/last (positional, including nullish). aggregationFn: 'auto' inspects the first core row's value: numbers → sum, dates → extent, anything else → unresolved.
  • Worker row models: eagerly compute explicitly configured grouped aggregates in the worker, but column.getAggregationValue() always runs its final total on the main thread over the selected row model — worker-crossing results must be structured-cloneable.

Code Examples

const features = tableFeatures({
  rowAggregationFeature, columnGroupingFeature,
  groupedRowModel: createGroupedRowModel(),
  aggregationFns: { sum: aggregationFn_sum, mean: aggregationFn_mean },
})

columnHelper.accessor('visits', {
  aggregationFn: 'sum',
  aggregatedCell: ({ getValue }) => getValue<number>().toLocaleString(),
  footer: ({ column }) => column.getAggregationValue<number>().toLocaleString(), // grand total, ungrouped default
})

columnHelper.accessor('score', {
  aggregationFn: ['count', 'mean', { id: 'range', aggregationFn: 'extent' }],   // multi-result
})
  • What it demonstrates: the same column carries both a per-group aggregatedCell (via grouped row model) and a grand-total footer (via the default no-args getAggregationValue()) — two independent uses of one registered sum function.

Key Takeaways

  1. Aggregation ≠ grouping — reach for aggregation alone whenever you need a grand/filtered total, and only add grouping when you actually need per-group rows.
  2. column.getAggregationValue() with no args is cached and grouping-independent; passing explicit { rows } always recomputes — pick deliberately based on how often the subset changes.
  3. Use merge on custom aggregation definitions once nested grouping is involved and the computation isn't a cheap reduce — it avoids re-scanning the same rows at every grouping level.

Connects To

  • Grouping (React) Guide: columnGroupingFeature and groupedRowModel, the other half of grouped aggregation.
  • Faceting (React) Guide: the related-but-distinct "filter metadata" concept, not a summary value.
  • Worker Row Models Guide (Experimental): how grouped aggregates cross the worker boundary.