@tanstack/react-table 9.0.0-alpha.9 → 9.0.0-beta.2
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/README.md +127 -0
- package/dist/FlexRender.cjs +61 -0
- package/dist/FlexRender.cjs.map +1 -0
- package/dist/FlexRender.d.cts +51 -0
- package/dist/FlexRender.d.ts +51 -0
- package/dist/FlexRender.js +58 -0
- package/dist/FlexRender.js.map +1 -0
- package/dist/Subscribe.cjs +13 -0
- package/dist/Subscribe.cjs.map +1 -0
- package/dist/Subscribe.d.cts +101 -0
- package/dist/Subscribe.d.ts +101 -0
- package/dist/Subscribe.js +13 -0
- package/dist/Subscribe.js.map +1 -0
- package/dist/_virtual/_rolldown/runtime.cjs +29 -0
- package/dist/createTableHook.cjs +313 -0
- package/dist/createTableHook.cjs.map +1 -0
- package/dist/createTableHook.d.cts +358 -0
- package/dist/createTableHook.d.ts +358 -0
- package/dist/createTableHook.js +311 -0
- package/dist/createTableHook.js.map +1 -0
- package/dist/flex-render.cjs +5 -0
- package/dist/flex-render.d.cts +2 -0
- package/dist/flex-render.d.ts +2 -0
- package/dist/flex-render.js +3 -0
- package/dist/index.cjs +18 -0
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +8 -0
- package/dist/legacy.cjs +14 -0
- package/dist/legacy.d.cts +2 -0
- package/dist/legacy.d.ts +2 -0
- package/dist/legacy.js +3 -0
- package/dist/reactivity.cjs +34 -0
- package/dist/reactivity.cjs.map +1 -0
- package/dist/reactivity.js +34 -0
- package/dist/reactivity.js.map +1 -0
- package/dist/static-functions.cjs +9 -0
- package/dist/static-functions.d.cts +1 -0
- package/dist/static-functions.d.ts +1 -0
- package/dist/static-functions.js +3 -0
- package/dist/useLegacyTable.cjs +191 -0
- package/dist/useLegacyTable.cjs.map +1 -0
- package/dist/useLegacyTable.d.cts +233 -0
- package/dist/useLegacyTable.d.ts +233 -0
- package/dist/useLegacyTable.js +181 -0
- package/dist/useLegacyTable.js.map +1 -0
- package/dist/useTable.cjs +72 -0
- package/dist/useTable.cjs.map +1 -0
- package/dist/useTable.d.cts +122 -0
- package/dist/useTable.d.ts +122 -0
- package/dist/useTable.js +72 -0
- package/dist/useTable.js.map +1 -0
- package/package.json +41 -22
- package/skills/react/client-to-server/SKILL.md +377 -0
- package/skills/react/compose-with-tanstack-form/SKILL.md +363 -0
- package/skills/react/compose-with-tanstack-pacer/SKILL.md +287 -0
- package/skills/react/compose-with-tanstack-query/SKILL.md +467 -0
- package/skills/react/compose-with-tanstack-store/SKILL.md +347 -0
- package/skills/react/compose-with-tanstack-virtual/SKILL.md +388 -0
- package/skills/react/compose-with-tanstack-virtual/references/column-virtualization-and-infinite-scroll.md +136 -0
- package/skills/react/getting-started/SKILL.md +388 -0
- package/skills/react/migrate-v8-to-v9/SKILL.md +488 -0
- package/skills/react/production-readiness/SKILL.md +341 -0
- package/skills/react/react-subscribe-compiler-compat/SKILL.md +269 -0
- package/skills/react/table-state/SKILL.md +432 -0
- package/src/FlexRender.tsx +136 -0
- package/src/Subscribe.ts +153 -0
- package/src/createTableHook.tsx +1121 -0
- package/src/flex-render.ts +1 -0
- package/src/index.ts +6 -0
- package/src/legacy.ts +3 -0
- package/src/reactivity.ts +41 -0
- package/src/static-functions.ts +1 -0
- package/src/useLegacyTable.ts +487 -0
- package/src/useTable.ts +191 -0
- package/dist/cjs/index.cjs +0 -77
- package/dist/cjs/index.cjs.map +0 -1
- package/dist/cjs/index.d.cts +0 -9
- package/dist/esm/index.d.ts +0 -9
- package/dist/esm/index.js +0 -55
- package/dist/esm/index.js.map +0 -1
- package/src/index.tsx +0 -92
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react/compose-with-tanstack-query
|
|
3
|
+
description: >
|
|
4
|
+
Server-side / async data flow for `@tanstack/react-table` v9 with
|
|
5
|
+
`@tanstack/react-query`. Canonical pattern: external pagination atom via
|
|
6
|
+
`useCreateAtom<PaginationState>` + `options.atoms` (NOT `state + on*Change`),
|
|
7
|
+
pagination object as part of `queryKey`, `manualPagination: true`,
|
|
8
|
+
`placeholderData: keepPreviousData` to avoid the 0-rows flash, and
|
|
9
|
+
`defaultData = useMemo(() => [], [])` to keep `data` reference stable
|
|
10
|
+
between fetches. `rowCount` from the API response so `getPageCount()` works.
|
|
11
|
+
type: composition
|
|
12
|
+
library: tanstack-table
|
|
13
|
+
framework: react
|
|
14
|
+
library_version: '9.0.0-alpha.48'
|
|
15
|
+
requires:
|
|
16
|
+
- react/client-to-server
|
|
17
|
+
- pagination
|
|
18
|
+
- react/table-state
|
|
19
|
+
sources:
|
|
20
|
+
- TanStack/table:examples/react/with-tanstack-query/src/main.tsx
|
|
21
|
+
- TanStack/table:examples/react/with-tanstack-query/src/fetchData.ts
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
This skill builds on `tanstack-table/state-management`, `tanstack-table/react/table-state`, and `tanstack-table/react/client-to-server`. Read those first — Query composition is `client-to-server` with a specific server.
|
|
25
|
+
|
|
26
|
+
## Why this pattern
|
|
27
|
+
|
|
28
|
+
A v9 React table written against TanStack Query has three load-bearing decisions:
|
|
29
|
+
|
|
30
|
+
1. **External pagination atom**, not `state` + `onPaginationChange`. Cleaner because the table writes to the atom directly; the query's `queryKey` watches the atom; refetches happen automatically.
|
|
31
|
+
2. **`placeholderData: keepPreviousData`** so the previous page stays visible while the next page fetches. Without it the table collapses to 0 rows on every page change and the scroll position jumps.
|
|
32
|
+
3. **Stable `data` fallback** (`defaultData = useMemo(() => [], [])`). `data: dataQuery.data?.rows ?? []` in JSX produces a new array each render and busts internal memos.
|
|
33
|
+
|
|
34
|
+
Source: `examples/react/with-tanstack-query/src/main.tsx`.
|
|
35
|
+
|
|
36
|
+
## Setup
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pnpm add @tanstack/react-table @tanstack/react-query @tanstack/react-store
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Mount one `<QueryClientProvider>` at the root:
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
46
|
+
|
|
47
|
+
const queryClient = new QueryClient()
|
|
48
|
+
|
|
49
|
+
ReactDOM.createRoot(rootElement).render(
|
|
50
|
+
<React.StrictMode>
|
|
51
|
+
<QueryClientProvider client={queryClient}>
|
|
52
|
+
<App />
|
|
53
|
+
</QueryClientProvider>
|
|
54
|
+
</React.StrictMode>,
|
|
55
|
+
)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Core Pattern — canonical server-paginated table
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import * as React from 'react'
|
|
62
|
+
import { keepPreviousData, useQuery } from '@tanstack/react-query'
|
|
63
|
+
import { useCreateAtom, useSelector } from '@tanstack/react-store'
|
|
64
|
+
import {
|
|
65
|
+
useTable,
|
|
66
|
+
tableFeatures,
|
|
67
|
+
rowPaginationFeature,
|
|
68
|
+
createColumnHelper,
|
|
69
|
+
} from '@tanstack/react-table'
|
|
70
|
+
import type { PaginationState } from '@tanstack/react-table'
|
|
71
|
+
import { fetchData } from './fetchData' // returns { rows, rowCount }
|
|
72
|
+
import type { Person } from './fetchData'
|
|
73
|
+
|
|
74
|
+
const features = tableFeatures({ rowPaginationFeature })
|
|
75
|
+
|
|
76
|
+
const columnHelper = createColumnHelper<typeof features, Person>()
|
|
77
|
+
const columns = columnHelper.columns([
|
|
78
|
+
columnHelper.accessor('firstName', {
|
|
79
|
+
header: 'First Name',
|
|
80
|
+
cell: (i) => i.getValue(),
|
|
81
|
+
}),
|
|
82
|
+
columnHelper.accessor('lastName', {
|
|
83
|
+
header: 'Last Name',
|
|
84
|
+
cell: (i) => i.getValue(),
|
|
85
|
+
}),
|
|
86
|
+
columnHelper.accessor('age', { header: 'Age' }),
|
|
87
|
+
columnHelper.accessor('visits', { header: 'Visits' }),
|
|
88
|
+
columnHelper.accessor('status', { header: 'Status' }),
|
|
89
|
+
columnHelper.accessor('progress', { header: 'Profile Progress' }),
|
|
90
|
+
])
|
|
91
|
+
|
|
92
|
+
function App() {
|
|
93
|
+
// 1) Pagination atom — stable identity via useCreateAtom.
|
|
94
|
+
const paginationAtom = useCreateAtom<PaginationState>({
|
|
95
|
+
pageIndex: 0,
|
|
96
|
+
pageSize: 10,
|
|
97
|
+
})
|
|
98
|
+
// 2) Subscribe so the query refetches on pagination changes.
|
|
99
|
+
const pagination = useSelector(paginationAtom, (s) => s)
|
|
100
|
+
|
|
101
|
+
// 3) Query keyed on the pagination object — refetch on every page/size change.
|
|
102
|
+
const dataQuery = useQuery({
|
|
103
|
+
queryKey: ['data', pagination],
|
|
104
|
+
queryFn: () => fetchData(pagination),
|
|
105
|
+
placeholderData: keepPreviousData, // 4) avoid 0-rows flash
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
// 5) Stable fallback — fresh `[]` in JSX would bust internal memos.
|
|
109
|
+
const defaultData = React.useMemo(() => [], [])
|
|
110
|
+
|
|
111
|
+
// 6) Manual pagination + rowCount; no paginatedRowModel.
|
|
112
|
+
const table = useTable(
|
|
113
|
+
{
|
|
114
|
+
features,
|
|
115
|
+
rowModels: {},
|
|
116
|
+
columns,
|
|
117
|
+
data: dataQuery.data?.rows ?? defaultData,
|
|
118
|
+
rowCount: dataQuery.data?.rowCount,
|
|
119
|
+
atoms: { pagination: paginationAtom }, // table writes here directly
|
|
120
|
+
manualPagination: true,
|
|
121
|
+
},
|
|
122
|
+
(state) => state,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<>
|
|
127
|
+
<table>
|
|
128
|
+
<thead>{/* table.FlexRender header={h} */}</thead>
|
|
129
|
+
<tbody>{/* table.FlexRender cell={c} */}</tbody>
|
|
130
|
+
</table>
|
|
131
|
+
<div className="controls">
|
|
132
|
+
<button
|
|
133
|
+
onClick={() => table.firstPage()}
|
|
134
|
+
disabled={!table.getCanPreviousPage()}
|
|
135
|
+
>
|
|
136
|
+
{'<<'}
|
|
137
|
+
</button>
|
|
138
|
+
<button
|
|
139
|
+
onClick={() => table.previousPage()}
|
|
140
|
+
disabled={!table.getCanPreviousPage()}
|
|
141
|
+
>
|
|
142
|
+
{'<'}
|
|
143
|
+
</button>
|
|
144
|
+
<button
|
|
145
|
+
onClick={() => table.nextPage()}
|
|
146
|
+
disabled={!table.getCanNextPage()}
|
|
147
|
+
>
|
|
148
|
+
{'>'}
|
|
149
|
+
</button>
|
|
150
|
+
<button
|
|
151
|
+
onClick={() => table.lastPage()}
|
|
152
|
+
disabled={!table.getCanNextPage()}
|
|
153
|
+
>
|
|
154
|
+
{'>>'}
|
|
155
|
+
</button>
|
|
156
|
+
<span>
|
|
157
|
+
Page{' '}
|
|
158
|
+
<strong>
|
|
159
|
+
{pagination.pageIndex + 1} of {table.getPageCount()}
|
|
160
|
+
</strong>
|
|
161
|
+
</span>
|
|
162
|
+
<select
|
|
163
|
+
value={pagination.pageSize}
|
|
164
|
+
onChange={(e) => table.setPageSize(Number(e.target.value))}
|
|
165
|
+
>
|
|
166
|
+
{[10, 20, 30, 40, 50].map((s) => (
|
|
167
|
+
<option key={s} value={s}>
|
|
168
|
+
Show {s}
|
|
169
|
+
</option>
|
|
170
|
+
))}
|
|
171
|
+
</select>
|
|
172
|
+
{dataQuery.isFetching ? 'Loading...' : null}
|
|
173
|
+
</div>
|
|
174
|
+
</>
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Source: `examples/react/with-tanstack-query/src/main.tsx` (this is the canonical example, near-verbatim).
|
|
180
|
+
|
|
181
|
+
## Adding sort + filter
|
|
182
|
+
|
|
183
|
+
The same pattern extends to multiple slices. Key the query on each, set the matching `manual*` flag, drop the matching `rowModels` factory.
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
const paginationAtom = useCreateAtom<PaginationState>({
|
|
187
|
+
pageIndex: 0,
|
|
188
|
+
pageSize: 10,
|
|
189
|
+
})
|
|
190
|
+
const sortingAtom = useCreateAtom<SortingState>([])
|
|
191
|
+
const columnFiltersAtom = useCreateAtom<ColumnFiltersState>([])
|
|
192
|
+
|
|
193
|
+
const pagination = useSelector(paginationAtom)
|
|
194
|
+
const sorting = useSelector(sortingAtom)
|
|
195
|
+
const columnFilters = useSelector(columnFiltersAtom)
|
|
196
|
+
|
|
197
|
+
const dataQuery = useQuery({
|
|
198
|
+
queryKey: ['data', { pagination, sorting, columnFilters }],
|
|
199
|
+
queryFn: () => fetchData({ pagination, sorting, columnFilters }),
|
|
200
|
+
placeholderData: keepPreviousData,
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
const table = useTable({
|
|
204
|
+
features: tableFeatures({
|
|
205
|
+
rowPaginationFeature,
|
|
206
|
+
rowSortingFeature,
|
|
207
|
+
columnFilteringFeature,
|
|
208
|
+
}),
|
|
209
|
+
rowModels: {}, // server owns sort/filter/paginate
|
|
210
|
+
columns,
|
|
211
|
+
data: dataQuery.data?.rows ?? defaultData,
|
|
212
|
+
rowCount: dataQuery.data?.rowCount,
|
|
213
|
+
atoms: {
|
|
214
|
+
pagination: paginationAtom,
|
|
215
|
+
sorting: sortingAtom,
|
|
216
|
+
columnFilters: columnFiltersAtom,
|
|
217
|
+
},
|
|
218
|
+
manualSorting: true,
|
|
219
|
+
manualFiltering: true,
|
|
220
|
+
manualPagination: true,
|
|
221
|
+
})
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Mutations and invalidation
|
|
225
|
+
|
|
226
|
+
TanStack Table is a downstream consumer — it has no way to know the server data changed. Call `queryClient.invalidateQueries` after mutations:
|
|
227
|
+
|
|
228
|
+
```tsx
|
|
229
|
+
const queryClient = useQueryClient()
|
|
230
|
+
const addPerson = useMutation({
|
|
231
|
+
mutationFn: createPerson,
|
|
232
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['data'] }),
|
|
233
|
+
})
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Common Mistakes
|
|
237
|
+
|
|
238
|
+
### CRITICAL Forgetting `manualPagination` / `manualSorting` / `manualFiltering`
|
|
239
|
+
|
|
240
|
+
Wrong:
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
const table = useTable({
|
|
244
|
+
features,
|
|
245
|
+
rowModels: { paginatedRowModel: createPaginatedRowModel() },
|
|
246
|
+
columns,
|
|
247
|
+
data: query.data?.rows ?? [],
|
|
248
|
+
// missing manualPagination
|
|
249
|
+
})
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Correct:
|
|
253
|
+
|
|
254
|
+
```tsx
|
|
255
|
+
const table = useTable({
|
|
256
|
+
features,
|
|
257
|
+
rowModels: {}, // drop paginatedRowModel
|
|
258
|
+
columns,
|
|
259
|
+
data: query.data?.rows ?? defaultData,
|
|
260
|
+
rowCount: query.data?.rowCount,
|
|
261
|
+
atoms: { pagination: paginationAtom },
|
|
262
|
+
manualPagination: true,
|
|
263
|
+
})
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Without `manualPagination: true`, the table re-paginates the server-already-paginated 10-row "dataset" — `getPageCount()` returns 1, and the pager locks at "Page 1 of 1".
|
|
267
|
+
Source: `examples/react/with-tanstack-query/src/main.tsx`.
|
|
268
|
+
|
|
269
|
+
### CRITICAL Missing `rowCount`
|
|
270
|
+
|
|
271
|
+
Wrong:
|
|
272
|
+
|
|
273
|
+
```tsx
|
|
274
|
+
const table = useTable({
|
|
275
|
+
features,
|
|
276
|
+
rowModels: {},
|
|
277
|
+
columns,
|
|
278
|
+
data: query.data?.rows ?? defaultData,
|
|
279
|
+
atoms: { pagination: paginationAtom },
|
|
280
|
+
manualPagination: true,
|
|
281
|
+
// missing rowCount
|
|
282
|
+
})
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Correct:
|
|
286
|
+
|
|
287
|
+
```tsx
|
|
288
|
+
const table = useTable({
|
|
289
|
+
features,
|
|
290
|
+
rowModels: {},
|
|
291
|
+
columns,
|
|
292
|
+
data: query.data?.rows ?? defaultData,
|
|
293
|
+
rowCount: query.data?.rowCount, // ← required for accurate pager
|
|
294
|
+
atoms: { pagination: paginationAtom },
|
|
295
|
+
manualPagination: true,
|
|
296
|
+
})
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
`getPageCount()` falls back to `Math.ceil(data.length / pageSize)` — which equals 1 when the server returned one page.
|
|
300
|
+
Source: `examples/react/with-tanstack-query/src/main.tsx`.
|
|
301
|
+
|
|
302
|
+
### CRITICAL `queryKey` doesn't include the pagination state
|
|
303
|
+
|
|
304
|
+
Wrong:
|
|
305
|
+
|
|
306
|
+
```tsx
|
|
307
|
+
useQuery({
|
|
308
|
+
queryKey: ['data'], // never changes
|
|
309
|
+
queryFn: () => fetchData(pagination),
|
|
310
|
+
})
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Correct:
|
|
314
|
+
|
|
315
|
+
```tsx
|
|
316
|
+
useQuery({
|
|
317
|
+
queryKey: ['data', pagination], // refetch on pagination change
|
|
318
|
+
queryFn: () => fetchData(pagination),
|
|
319
|
+
placeholderData: keepPreviousData,
|
|
320
|
+
})
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Query has no way to know its inputs changed unless they're in `queryKey`. Pager button clicks update the atom but the query never refetches.
|
|
324
|
+
Source: `examples/react/with-tanstack-query/src/main.tsx`.
|
|
325
|
+
|
|
326
|
+
### HIGH Skipping `placeholderData: keepPreviousData`
|
|
327
|
+
|
|
328
|
+
Wrong:
|
|
329
|
+
|
|
330
|
+
```tsx
|
|
331
|
+
useQuery({
|
|
332
|
+
queryKey: ['data', pagination],
|
|
333
|
+
queryFn: () => fetchData(pagination),
|
|
334
|
+
})
|
|
335
|
+
// Between pages: table renders 0 rows, container collapses, scroll position jumps.
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Correct:
|
|
339
|
+
|
|
340
|
+
```tsx
|
|
341
|
+
useQuery({
|
|
342
|
+
queryKey: ['data', pagination],
|
|
343
|
+
queryFn: () => fetchData(pagination),
|
|
344
|
+
placeholderData: keepPreviousData, // previous page stays visible while fetching
|
|
345
|
+
})
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
The previous page renders during the fetch — no flash, no jump.
|
|
349
|
+
Source: `examples/react/with-tanstack-query/src/main.tsx`.
|
|
350
|
+
|
|
351
|
+
### HIGH Recreating `data: query.data?.rows ?? []` in JSX
|
|
352
|
+
|
|
353
|
+
Wrong:
|
|
354
|
+
|
|
355
|
+
```tsx
|
|
356
|
+
const table = useTable({
|
|
357
|
+
features,
|
|
358
|
+
rowModels: {},
|
|
359
|
+
columns,
|
|
360
|
+
data: query.data?.rows ?? [], // new identity every render
|
|
361
|
+
// ...
|
|
362
|
+
})
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Correct:
|
|
366
|
+
|
|
367
|
+
```tsx
|
|
368
|
+
const defaultData = React.useMemo(() => [], [])
|
|
369
|
+
// or: const EMPTY: Person[] = [] at module scope
|
|
370
|
+
|
|
371
|
+
const table = useTable({
|
|
372
|
+
features,
|
|
373
|
+
rowModels: {},
|
|
374
|
+
columns,
|
|
375
|
+
data: query.data?.rows ?? defaultData,
|
|
376
|
+
// ...
|
|
377
|
+
})
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
`?? []` creates a fresh array reference each render, busting internal memos that depend on `data` identity.
|
|
381
|
+
Source: `examples/react/with-tanstack-query/src/main.tsx` (uses `useMemo`).
|
|
382
|
+
|
|
383
|
+
### HIGH Mixing `state.pagination` + `onPaginationChange` AND `atoms.pagination`
|
|
384
|
+
|
|
385
|
+
Wrong:
|
|
386
|
+
|
|
387
|
+
```tsx
|
|
388
|
+
useTable({
|
|
389
|
+
features,
|
|
390
|
+
rowModels: {},
|
|
391
|
+
columns,
|
|
392
|
+
data,
|
|
393
|
+
state: { pagination }, // silently ignored
|
|
394
|
+
onPaginationChange: setPagination, // silently ignored
|
|
395
|
+
atoms: { pagination: paginationAtom }, // wins
|
|
396
|
+
manualPagination: true,
|
|
397
|
+
})
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Correct:
|
|
401
|
+
|
|
402
|
+
```tsx
|
|
403
|
+
// Pick one. The atom pattern is canonical for Query.
|
|
404
|
+
useTable({
|
|
405
|
+
features,
|
|
406
|
+
rowModels: {},
|
|
407
|
+
columns,
|
|
408
|
+
data,
|
|
409
|
+
atoms: { pagination: paginationAtom },
|
|
410
|
+
manualPagination: true,
|
|
411
|
+
})
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
Precedence is `atoms` > `state` > internal. The `state` plumbing is dead.
|
|
415
|
+
Source: `examples/react/basic-external-atoms/src/main.tsx`.
|
|
416
|
+
|
|
417
|
+
### HIGH Forgetting `invalidateQueries` after mutations
|
|
418
|
+
|
|
419
|
+
Wrong:
|
|
420
|
+
|
|
421
|
+
```tsx
|
|
422
|
+
const addPerson = useMutation({
|
|
423
|
+
mutationFn: createPerson,
|
|
424
|
+
// missing onSuccess invalidation
|
|
425
|
+
})
|
|
426
|
+
// Table never sees the new row.
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
Correct:
|
|
430
|
+
|
|
431
|
+
```tsx
|
|
432
|
+
const queryClient = useQueryClient()
|
|
433
|
+
const addPerson = useMutation({
|
|
434
|
+
mutationFn: createPerson,
|
|
435
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['data'] }),
|
|
436
|
+
})
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
The table is downstream of Query. Mutations must invalidate the relevant query keys.
|
|
440
|
+
Source: docs/framework/react/react-query.
|
|
441
|
+
|
|
442
|
+
### MEDIUM Leaving `paginatedRowModel` registered when the server paginates
|
|
443
|
+
|
|
444
|
+
Wrong:
|
|
445
|
+
|
|
446
|
+
```tsx
|
|
447
|
+
rowModels: {
|
|
448
|
+
paginatedRowModel: createPaginatedRowModel()
|
|
449
|
+
} // ships unused code
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
Correct:
|
|
453
|
+
|
|
454
|
+
```tsx
|
|
455
|
+
rowModels: {
|
|
456
|
+
} // server paginates; drop the factory
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Bundle waste plus a foot-gun if `manualPagination` is ever flipped off.
|
|
460
|
+
Source: `examples/react/with-tanstack-query/src/main.tsx`.
|
|
461
|
+
|
|
462
|
+
## See Also
|
|
463
|
+
|
|
464
|
+
- `tanstack-table/react/client-to-server` — the underlying manual-mode mechanics.
|
|
465
|
+
- `tanstack-table/react/compose-with-tanstack-store` — owning state slices via atoms.
|
|
466
|
+
- `tanstack-table/react/compose-with-tanstack-virtual` — infinite scroll = Virtual + `useInfiniteQuery`.
|
|
467
|
+
- `tanstack-table/react/compose-with-tanstack-pacer` — debounce filter writes that feed the query.
|