@tanstack/react-table 9.0.0-alpha.32 → 9.0.0-alpha.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Subscribe.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  'use client'
2
2
 
3
- import { shallow, useStore } from '@tanstack/react-store'
3
+ import { shallow, useSelector } from '@tanstack/react-store'
4
+ import type { Atom, ReadonlyAtom } from '@tanstack/react-store'
4
5
  import type {
5
- NoInfer,
6
6
  RowData,
7
7
  Table,
8
8
  TableFeatures,
@@ -10,35 +10,94 @@ import type {
10
10
  } from '@tanstack/table-core'
11
11
  import type { FunctionComponent, ReactNode } from 'react'
12
12
 
13
- export type SubscribeProps<
13
+ /**
14
+ * Subscribe to `table.store` (full table state). The selector receives the full
15
+ * {@link TableState}.
16
+ */
17
+ export type SubscribePropsWithStore<
14
18
  TFeatures extends TableFeatures,
15
19
  TData extends RowData,
16
- TSelected = {},
20
+ TSelected,
17
21
  > = {
18
- /**
19
- * The table instance to subscribe to. Required when using as a standalone component.
20
- * Not needed when using as `table.Subscribe`.
21
- */
22
22
  table: Table<TFeatures, TData>
23
23
  /**
24
- * A selector function that selects the part of the table state to subscribe to.
25
- * This allows for fine-grained reactivity by only re-rendering when the selected state changes.
26
- */
27
- selector: (state: NoInfer<TableState<TFeatures>>) => TSelected
28
- /**
29
- * The children to render. Can be a function that receives the selected state, or a React node.
24
+ * Select from full table state. Re-renders when the selected value changes
25
+ * (shallow compare).
26
+ *
27
+ * Required in store mode so you never accidentally subscribe to the whole
28
+ * store without an explicit projection.
30
29
  */
30
+ selector: (state: TableState<TFeatures>) => TSelected
31
31
  children: ((state: TSelected) => ReactNode) | ReactNode
32
32
  }
33
33
 
34
+ /**
35
+ * Subscribe to the full value of a slice atom (e.g. `table.atoms.rowSelection`).
36
+ * Omitting `selector` is equivalent to the identity selector — children receive
37
+ * `TAtomValue`.
38
+ */
39
+ export type SubscribePropsWithAtomIdentity<
40
+ TFeatures extends TableFeatures,
41
+ TData extends RowData,
42
+ TAtomValue,
43
+ > = {
44
+ table: Table<TFeatures, TData>
45
+ atom: Atom<TAtomValue> | ReadonlyAtom<TAtomValue>
46
+ selector?: undefined
47
+ children: ((state: TAtomValue) => ReactNode) | ReactNode
48
+ }
49
+
50
+ /**
51
+ * Subscribe to a projected value from a slice atom. The selector receives the
52
+ * atom value; children receive the projected `TSelected`.
53
+ */
54
+ export type SubscribePropsWithAtomWithSelector<
55
+ TFeatures extends TableFeatures,
56
+ TData extends RowData,
57
+ TAtomValue,
58
+ TSelected,
59
+ > = {
60
+ table: Table<TFeatures, TData>
61
+ atom: Atom<TAtomValue> | ReadonlyAtom<TAtomValue>
62
+ selector: (state: TAtomValue) => TSelected
63
+ children: ((state: TSelected) => ReactNode) | ReactNode
64
+ }
65
+
66
+ /**
67
+ * Subscribe to a single slice atom (identity or projected). Prefer
68
+ * {@link SubscribePropsWithAtomIdentity} or {@link SubscribePropsWithAtomWithSelector}
69
+ * for clearer inference when `selector` is omitted.
70
+ */
71
+ export type SubscribePropsWithAtom<
72
+ TFeatures extends TableFeatures,
73
+ TData extends RowData,
74
+ TAtomValue,
75
+ TSelected = TAtomValue,
76
+ > =
77
+ | SubscribePropsWithAtomIdentity<TFeatures, TData, TAtomValue>
78
+ | SubscribePropsWithAtomWithSelector<TFeatures, TData, TAtomValue, TSelected>
79
+
80
+ export type SubscribeProps<
81
+ TFeatures extends TableFeatures,
82
+ TData extends RowData,
83
+ TSelected = unknown,
84
+ TAtomValue = unknown,
85
+ > =
86
+ | SubscribePropsWithStore<TFeatures, TData, TSelected>
87
+ | SubscribePropsWithAtomIdentity<TFeatures, TData, TAtomValue>
88
+ | SubscribePropsWithAtomWithSelector<TFeatures, TData, TAtomValue, TSelected>
89
+
34
90
  /**
35
91
  * A React component that allows you to subscribe to the table state.
36
92
  *
37
93
  * This is useful for opting into state re-renders for specific parts of the table state.
38
94
  *
95
+ * For `table.Subscribe` from `useTable`, prefer that API — it uses overloads so JSX
96
+ * contextual typing works. This standalone component uses a union `props` type.
97
+ *
39
98
  * @example
40
99
  * ```tsx
41
- * // As a standalone component
100
+ * // As a standalone component — full store
42
101
  * <Subscribe table={table} selector={(state) => ({ rowSelection: state.rowSelection })}>
43
102
  * {({ rowSelection }) => (
44
103
  * <div>Selected rows: {Object.keys(rowSelection).length}</div>
@@ -48,6 +107,26 @@ export type SubscribeProps<
48
107
  *
49
108
  * @example
50
109
  * ```tsx
110
+ * // Entire slice atom (no selector)
111
+ * <Subscribe table={table} atom={table.atoms.rowSelection}>
112
+ * {(rowSelection) => <div>...</div>}
113
+ * </Subscribe>
114
+ * ```
115
+ *
116
+ * @example
117
+ * ```tsx
118
+ * // Project atom value (e.g. one row’s selection)
119
+ * <Subscribe
120
+ * table={table}
121
+ * atom={table.atoms.rowSelection}
122
+ * selector={(rowSelection) => rowSelection?.[row.id]}
123
+ * >
124
+ * {(selected) => <tr data-selected={!!selected}>...</tr>}
125
+ * </Subscribe>
126
+ * ```
127
+ *
128
+ * @example
129
+ * ```tsx
51
130
  * // As table.Subscribe (table instance method)
52
131
  * <table.Subscribe selector={(state) => ({ rowSelection: state.rowSelection })}>
53
132
  * {({ rowSelection }) => (
@@ -59,13 +138,52 @@ export type SubscribeProps<
59
138
  export function Subscribe<
60
139
  TFeatures extends TableFeatures,
61
140
  TData extends RowData,
62
- TSelected = {},
141
+ TAtomValue,
142
+ >(
143
+ props: SubscribePropsWithAtomIdentity<TFeatures, TData, TAtomValue>,
144
+ ): ReturnType<FunctionComponent>
145
+ export function Subscribe<
146
+ TFeatures extends TableFeatures,
147
+ TData extends RowData,
148
+ TAtomValue,
149
+ TSelected,
150
+ >(
151
+ props: SubscribePropsWithAtomWithSelector<
152
+ TFeatures,
153
+ TData,
154
+ TAtomValue,
155
+ TSelected
156
+ >,
157
+ ): ReturnType<FunctionComponent>
158
+ export function Subscribe<
159
+ TFeatures extends TableFeatures,
160
+ TData extends RowData,
161
+ TSelected,
63
162
  >(
64
- props: SubscribeProps<TFeatures, TData, TSelected>,
163
+ props: SubscribePropsWithStore<TFeatures, TData, TSelected>,
164
+ ): ReturnType<FunctionComponent>
165
+ export function Subscribe<
166
+ TFeatures extends TableFeatures,
167
+ TData extends RowData,
168
+ TSelected,
169
+ TAtomValue,
170
+ >(
171
+ props: SubscribeProps<TFeatures, TData, TSelected, TAtomValue>,
65
172
  ): ReturnType<FunctionComponent> {
66
- const selected = useStore(props.table.store, props.selector, shallow)
173
+ const source = 'atom' in props ? props.atom : props.table.store
174
+ const selectFn =
175
+ 'atom' in props ? (props.selector ?? ((x: unknown) => x)) : props.selector
176
+
177
+ const selected = useSelector(
178
+ // Atom and store share the same selection protocol; union args need a widen for TS.
179
+ source,
180
+ selectFn as Parameters<typeof useSelector>[1],
181
+ {
182
+ compare: shallow,
183
+ },
184
+ ) as TSelected
67
185
 
68
186
  return typeof props.children === 'function'
69
- ? props.children(selected)
187
+ ? (props.children as (state: TSelected) => ReactNode)(selected)
70
188
  : props.children
71
189
  }
@@ -0,0 +1 @@
1
+ export * from '@tanstack/table-core/static-functions'
@@ -16,18 +16,20 @@ import {
16
16
  stockFeatures,
17
17
  } from '@tanstack/table-core'
18
18
  import { useCallback, useMemo } from 'react'
19
- import { shallow, useStore } from '@tanstack/react-store'
20
19
  import { useTable } from './useTable'
21
20
  import type {
21
+ AggregationFns,
22
22
  Cell,
23
23
  Column,
24
24
  ColumnDef,
25
25
  CreateRowModels_All,
26
+ FilterFns,
26
27
  Header,
27
28
  HeaderGroup,
28
29
  Row,
29
30
  RowData,
30
31
  RowModel,
32
+ SortFns,
31
33
  StockFeatures,
32
34
  Table,
33
35
  TableOptions,
@@ -234,6 +236,21 @@ export interface LegacyRowModelOptions<TData extends RowData> {
234
236
  * @deprecated Use `_rowModels.facetedUniqueValues` with `createFacetedUniqueValues()` instead.
235
237
  */
236
238
  getFacetedUniqueValues?: FacetedUniqueValuesFactory<TData>
239
+ /**
240
+ * Additional filter functions to apply to the table.
241
+ * @deprecated Use `_rowModels.filteredRowModel` with `createFilteredRowModel(filterFns)` instead.
242
+ */
243
+ filterFns?: FilterFns
244
+ /**
245
+ * Additional sort functions to apply to the table.
246
+ * @deprecated Use `_rowModels.sortedRowModel` with `createSortedRowModel(sortFns)` instead.
247
+ */
248
+ sortFns?: SortFns
249
+ /**
250
+ * Additional aggregation functions to apply to the table.
251
+ * @deprecated Use `_rowModels.groupedRowModel` with `createGroupedRowModel(aggregationFns)` instead.
252
+ */
253
+ aggregationFns?: AggregationFns
237
254
  }
238
255
 
239
256
  /**
@@ -265,6 +282,11 @@ export type LegacyReactTable<TData extends RowData> = ReactTable<
265
282
  * @deprecated In v9, access state directly via `table.state` or use `table.store.state` for the full state.
266
283
  */
267
284
  getState: () => TableState<StockFeatures>
285
+ /**
286
+ * Sets the current table state.
287
+ * @deprecated In v9, access state directly via `table.baseAtoms`
288
+ */
289
+ setState: (state: TableState<StockFeatures>) => void
268
290
  }
269
291
 
270
292
  // =============================================================================
@@ -387,11 +409,17 @@ export function useLegacyTable<TData extends RowData>(
387
409
  // Note: getCoreRowModel is handled automatically in v9, so we ignore it
388
410
 
389
411
  if (getFilteredRowModel) {
390
- _rowModels.filteredRowModel = createFilteredRowModel(filterFns)
412
+ _rowModels.filteredRowModel = createFilteredRowModel({
413
+ ...filterFns,
414
+ ...options.filterFns,
415
+ })
391
416
  }
392
417
 
393
418
  if (getSortedRowModel) {
394
- _rowModels.sortedRowModel = createSortedRowModel(sortFns)
419
+ _rowModels.sortedRowModel = createSortedRowModel({
420
+ ...sortFns,
421
+ ...options.sortFns,
422
+ })
395
423
  }
396
424
 
397
425
  if (getPaginationRowModel) {
@@ -403,7 +431,10 @@ export function useLegacyTable<TData extends RowData>(
403
431
  }
404
432
 
405
433
  if (getGroupedRowModel) {
406
- _rowModels.groupedRowModel = createGroupedRowModel(aggregationFns)
434
+ _rowModels.groupedRowModel = createGroupedRowModel({
435
+ ...aggregationFns,
436
+ ...options.aggregationFns,
437
+ })
407
438
  }
408
439
 
409
440
  if (getFacetedRowModel) {
@@ -428,12 +459,32 @@ export function useLegacyTable<TData extends RowData>(
428
459
  (state) => state,
429
460
  )
430
461
 
462
+ const getState = useCallback(() => {
463
+ // all state except for columns and data
464
+ return {
465
+ ...table.state,
466
+ columns: undefined,
467
+ data: undefined,
468
+ }
469
+ }, [table])
470
+
471
+ const setState = useCallback(
472
+ (state: TableState<StockFeatures>) => {
473
+ Object.entries(state).forEach(([key, value]) => {
474
+ // @ts-expect-error - baseAtoms is indexed by dynamic string keys
475
+ table.baseAtoms[key].set(value)
476
+ })
477
+ },
478
+ [table],
479
+ )
480
+
431
481
  return useMemo(
432
482
  () =>
433
483
  ({
434
484
  ...table,
435
- getState: () => table.state,
485
+ getState,
486
+ setState,
436
487
  }) as LegacyReactTable<TData>,
437
- [table],
488
+ [table, getState, setState],
438
489
  )
439
490
  }
package/src/useTable.ts CHANGED
@@ -2,21 +2,21 @@
2
2
 
3
3
  import { useMemo, useState } from 'react'
4
4
  import { constructTable } from '@tanstack/table-core'
5
- import { shallow, useStore } from '@tanstack/react-store'
5
+ import { shallow, useSelector } from '@tanstack/react-store'
6
6
  import { FlexRender } from './FlexRender'
7
7
  import { Subscribe } from './Subscribe'
8
8
  import type {
9
9
  CellData,
10
- NoInfer,
11
10
  RowData,
12
11
  Table,
13
12
  TableFeatures,
14
13
  TableOptions,
15
14
  TableState,
16
15
  } from '@tanstack/table-core'
16
+ import type { Atom, ReadonlyAtom } from '@tanstack/react-store'
17
17
  import type { FunctionComponent, ReactNode } from 'react'
18
18
  import type { FlexRenderProps } from './FlexRender'
19
- import type { SubscribeProps } from './Subscribe'
19
+ import type { SubscribePropsWithStore } from './Subscribe'
20
20
 
21
21
  export type ReactTable<
22
22
  TFeatures extends TableFeatures,
@@ -28,19 +28,53 @@ export type ReactTable<
28
28
  *
29
29
  * This is useful for opting into state re-renders for specific parts of the table state.
30
30
  *
31
+ * Pass `atom` to subscribe to a single slice atom instead of the full `table.store`.
32
+ *
31
33
  * @example
32
34
  * <table.Subscribe selector={(state) => ({ rowSelection: state.rowSelection })}>
33
- * {({ rowSelection }) => ( // important to include `{() => {()}}` syntax
34
- * <tr key={row.id}>
35
- // render the row
36
- * </tr>
37
- * ))}
35
+ * {({ rowSelection }) => (
36
+ * <tr key={row.id}>...</tr>
37
+ * )}
38
+ * </table.Subscribe>
39
+ *
40
+ * @example
41
+ * <table.Subscribe atom={table.atoms.rowSelection}>
42
+ * {(rowSelection) => <div>...</div>}
43
+ * </table.Subscribe>
44
+ *
45
+ * @example
46
+ * <table.Subscribe atom={table.atoms.rowSelection} selector={(s) => s?.[row.id]}>
47
+ * {() => <tr key={row.id}>...</tr>}
38
48
  * </table.Subscribe>
39
49
  */
40
- Subscribe: <TSelected>(props: {
41
- selector: (state: NoInfer<TableState<TFeatures>>) => TSelected
42
- children: ((state: TSelected) => ReactNode) | ReactNode
43
- }) => ReturnType<FunctionComponent>
50
+ /**
51
+ * Overloads (not a single union) so `selector` callbacks get correct contextual
52
+ * types in JSX; a union of two `selector` signatures degrades to implicit `any`.
53
+ *
54
+ * Atom **without** `selector` is a separate overload so children receive `TAtomValue`
55
+ * (identity projection). If `selector` were optional on one overload, `TSubSelected`
56
+ * would default to `unknown` instead of inferring from the atom.
57
+ *
58
+ * The **atom** overloads are listed first so `TAtomValue` is inferred from `atom`.
59
+ */
60
+ Subscribe: {
61
+ <TAtomValue>(props: {
62
+ atom: Atom<TAtomValue> | ReadonlyAtom<TAtomValue>
63
+ selector?: undefined
64
+ children: ((state: TAtomValue) => ReactNode) | ReactNode
65
+ }): ReturnType<FunctionComponent>
66
+ <TAtomValue, TSubSelected>(props: {
67
+ atom: Atom<TAtomValue> | ReadonlyAtom<TAtomValue>
68
+ selector: (state: TAtomValue) => TSubSelected
69
+ children: ((state: TSubSelected) => ReactNode) | ReactNode
70
+ }): ReturnType<FunctionComponent>
71
+ <TSubSelected>(
72
+ props: Omit<
73
+ SubscribePropsWithStore<TFeatures, TData, TSubSelected>,
74
+ 'table'
75
+ >,
76
+ ): ReturnType<FunctionComponent>
77
+ }
44
78
  /**
45
79
  * A React component that renders headers, cells, or footers with custom markup.
46
80
  * Use this utility component instead of manually calling flexRender.
@@ -92,11 +126,12 @@ export function useTable<
92
126
  TSelected
93
127
  >
94
128
 
95
- tableInstance.Subscribe = function SubscribeBound<TSelected>(
96
- props: Omit<SubscribeProps<TFeatures, TData, TSelected>, 'table'>,
97
- ) {
98
- return Subscribe({ ...props, table: tableInstance })
99
- }
129
+ tableInstance.Subscribe = ((props: any) => {
130
+ return Subscribe({
131
+ ...props,
132
+ table: tableInstance,
133
+ })
134
+ }) as ReactTable<TFeatures, TData, TSelected>['Subscribe']
100
135
 
101
136
  tableInstance.FlexRender = FlexRender
102
137
 
@@ -115,7 +150,9 @@ export function useTable<
115
150
  ...selector(state),
116
151
  })
117
152
 
118
- const state = useStore(table.store, selectorWithDataAndColumns, shallow)
153
+ const state = useSelector(table.store, selectorWithDataAndColumns, {
154
+ compare: shallow,
155
+ })
119
156
 
120
157
  return useMemo(
121
158
  () => ({