@tanstack/preact-table 9.0.0-alpha.46 → 9.0.0-alpha.48
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 +10 -0
- package/dist/reactivity.cjs +1 -0
- package/dist/reactivity.cjs.map +1 -1
- package/dist/reactivity.js +1 -0
- package/dist/reactivity.js.map +1 -1
- package/package.json +6 -4
- package/skills/preact/client-to-server/SKILL.md +294 -0
- package/skills/preact/compose-with-tanstack-form/SKILL.md +230 -0
- package/skills/preact/compose-with-tanstack-pacer/SKILL.md +186 -0
- package/skills/preact/compose-with-tanstack-query/SKILL.md +283 -0
- package/skills/preact/compose-with-tanstack-store/SKILL.md +263 -0
- package/skills/preact/compose-with-tanstack-virtual/SKILL.md +275 -0
- package/skills/preact/getting-started/SKILL.md +371 -0
- package/skills/preact/migrate-v8-to-v9/SKILL.md +322 -0
- package/skills/preact/production-readiness/SKILL.md +278 -0
- package/skills/preact/table-state/SKILL.md +432 -0
- package/skills/preact/table-state/references/advanced-state-patterns.md +93 -0
- package/src/reactivity.ts +1 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: preact/compose-with-tanstack-pacer
|
|
3
|
+
description: >
|
|
4
|
+
Use `@tanstack/preact-pacer` to debounce / throttle the high-frequency writes
|
|
5
|
+
that drive an interactive table: column filter inputs and column resize
|
|
6
|
+
state. Pattern: import `useDebouncedCallback` from
|
|
7
|
+
`@tanstack/preact-pacer/debouncer` (or `useThrottledCallback` for resize),
|
|
8
|
+
wrap your `column.setFilterValue` / `table.setColumnSizing` calls, and let
|
|
9
|
+
the table's expensive row-model recompute happen on the trailing edge.
|
|
10
|
+
Routing keywords: preact-pacer, debounce filter, throttle resize, useDebouncedCallback,
|
|
11
|
+
performant filtering.
|
|
12
|
+
type: composition
|
|
13
|
+
library: tanstack-table
|
|
14
|
+
framework: preact
|
|
15
|
+
library_version: '9.0.0-alpha.47'
|
|
16
|
+
requires:
|
|
17
|
+
- filtering
|
|
18
|
+
- column-layout
|
|
19
|
+
sources:
|
|
20
|
+
- TanStack/table:examples/preact/filters/
|
|
21
|
+
- TanStack/table:examples/preact/column-resizing-performant/
|
|
22
|
+
- TanStack/table:examples/react/with-tanstack-form/
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
Filtering and column resizing fire a lot of events. Each `column.setFilterValue(...)` invalidates `filteredRowModel`, then `paginatedRowModel`, then re-renders subscribed components. For large tables that is the difference between a snappy UI and a janky one. Debounce or throttle the writes with @tanstack/preact-pacer.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @tanstack/preact-pacer
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Pattern 1 — Debounced column filter input
|
|
34
|
+
|
|
35
|
+
Render the input value from local state (immediate visual feedback) and push the value into the table on a debounce.
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { useState } from 'preact/hooks'
|
|
39
|
+
import { useDebouncedCallback } from '@tanstack/preact-pacer/debouncer'
|
|
40
|
+
import type { Column } from '@tanstack/preact-table'
|
|
41
|
+
|
|
42
|
+
function FilterInput<TFeatures, TData>({
|
|
43
|
+
column,
|
|
44
|
+
}: {
|
|
45
|
+
column: Column<TFeatures, TData>
|
|
46
|
+
}) {
|
|
47
|
+
const initial = (column.getFilterValue() as string | undefined) ?? ''
|
|
48
|
+
const [value, setValue] = useState(initial)
|
|
49
|
+
|
|
50
|
+
const pushToTable = useDebouncedCallback(
|
|
51
|
+
(next: string) => column.setFilterValue(next),
|
|
52
|
+
{ wait: 250 },
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<input
|
|
57
|
+
type="text"
|
|
58
|
+
value={value}
|
|
59
|
+
onInput={(e) => {
|
|
60
|
+
const next = (e.target as HTMLInputElement).value
|
|
61
|
+
setValue(next)
|
|
62
|
+
pushToTable(next)
|
|
63
|
+
}}
|
|
64
|
+
placeholder="Search…"
|
|
65
|
+
/>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The local `value` keeps the cursor and typing responsive; `pushToTable` only runs on the trailing edge, so the table only recomputes the filtered row model once per pause.
|
|
71
|
+
|
|
72
|
+
Same pattern works for the global filter via `table.setGlobalFilter`.
|
|
73
|
+
|
|
74
|
+
Source: `examples/preact/filters/`.
|
|
75
|
+
|
|
76
|
+
## Pattern 2 — Throttled column resize
|
|
77
|
+
|
|
78
|
+
Column resize fires per-mousemove. Throttling keeps the resize visually smooth without re-running the full layout on every pixel.
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
import { useThrottledCallback } from '@tanstack/preact-pacer/throttler'
|
|
82
|
+
|
|
83
|
+
function ResizeHandle({ header, table }) {
|
|
84
|
+
const pushSize = useThrottledCallback(
|
|
85
|
+
(size: number) => {
|
|
86
|
+
table.setColumnSizing((prev) => ({ ...prev, [header.column.id]: size }))
|
|
87
|
+
},
|
|
88
|
+
{ wait: 16 }, // ~60fps
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
// Hook this into your existing pointermove handler.
|
|
92
|
+
return null
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
In v9, column sizing is driven by `columnSizingFeature` + `table.setColumnSizing`. The throttle wraps the write side; the read side stays direct.
|
|
97
|
+
|
|
98
|
+
Source: `examples/preact/column-resizing-performant/`.
|
|
99
|
+
|
|
100
|
+
## When NOT to debounce
|
|
101
|
+
|
|
102
|
+
- Sorting (`table.setSorting`) — fires once per click.
|
|
103
|
+
- Pagination (`table.setPageIndex`, `table.setPageSize`) — fires once per page.
|
|
104
|
+
- Row selection (`row.toggleSelected`) — fires once per toggle.
|
|
105
|
+
|
|
106
|
+
Debouncing these adds latency for no win. Reach for pacer only on input/resize/scroll-driven writes.
|
|
107
|
+
|
|
108
|
+
## With External Atoms
|
|
109
|
+
|
|
110
|
+
If you've moved a slice to an external atom, debounce the atom write instead of the table API.
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
const globalFilterAtom = useCreateAtom<string>('')
|
|
114
|
+
|
|
115
|
+
const pushFilter = useDebouncedCallback(
|
|
116
|
+
(next: string) => globalFilterAtom.set(next),
|
|
117
|
+
{ wait: 250 },
|
|
118
|
+
)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The table reads from the atom; the atom now changes less often; the row model recomputes less often.
|
|
122
|
+
|
|
123
|
+
## Common Mistakes
|
|
124
|
+
|
|
125
|
+
### CRITICAL Wrapping `column.setFilterValue` AND reading filter via `column.getFilterValue()` in the same input
|
|
126
|
+
|
|
127
|
+
Wrong:
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
const debouncedSet = useDebouncedCallback(
|
|
131
|
+
(v: string) => column.setFilterValue(v),
|
|
132
|
+
{ wait: 250 },
|
|
133
|
+
)
|
|
134
|
+
return (
|
|
135
|
+
<input
|
|
136
|
+
value={(column.getFilterValue() ?? '') as string} // doesn't reflect typed value
|
|
137
|
+
onInput={(e) => debouncedSet((e.target as HTMLInputElement).value)}
|
|
138
|
+
/>
|
|
139
|
+
)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Correct: hold local state for the visual `value`, debounce the write.
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
const [v, setV] = useState((column.getFilterValue() ?? '') as string)
|
|
146
|
+
const debouncedSet = useDebouncedCallback(
|
|
147
|
+
(next: string) => column.setFilterValue(next),
|
|
148
|
+
{ wait: 250 },
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
return (
|
|
152
|
+
<input
|
|
153
|
+
value={v}
|
|
154
|
+
onInput={(e) => {
|
|
155
|
+
const next = (e.target as HTMLInputElement).value
|
|
156
|
+
setV(next)
|
|
157
|
+
debouncedSet(next)
|
|
158
|
+
}}
|
|
159
|
+
/>
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Otherwise the input lags by the debounce wait — the user sees stale characters.
|
|
164
|
+
|
|
165
|
+
### HIGH Debouncing the wrong direction
|
|
166
|
+
|
|
167
|
+
Wrong: debouncing the read (`column.getFilterValue`), which is just a memoized derivation.
|
|
168
|
+
|
|
169
|
+
Correct: debounce the **write** (`column.setFilterValue`) — that's what triggers the row-model recompute.
|
|
170
|
+
|
|
171
|
+
### HIGH Treating pacer as a substitute for stable references
|
|
172
|
+
|
|
173
|
+
Wrong: debouncing every `useTable` call.
|
|
174
|
+
|
|
175
|
+
Correct: pacer doesn't fix unstable `_features` / `columns` / `data` references. Stabilize those first; reach for pacer for input/scroll/resize hotspots.
|
|
176
|
+
Source: `docs/framework/preact/guide/table-state.md` (FAQ #1).
|
|
177
|
+
|
|
178
|
+
### MEDIUM Forgetting that debounce delays the trailing edge
|
|
179
|
+
|
|
180
|
+
The first keystroke still hits the table at the trailing edge of the debounce window. If you want a leading-edge call (e.g. fire immediately, then debounce subsequent calls), use the `{ leading: true, trailing: true }` shape.
|
|
181
|
+
|
|
182
|
+
## See Also
|
|
183
|
+
|
|
184
|
+
- `tanstack-table/filtering` — column / global filter state shape.
|
|
185
|
+
- `tanstack-table/column-layout` — column sizing / pinning / visibility.
|
|
186
|
+
- `tanstack-table/preact/production-readiness` — narrow selectors, stable refs.
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: preact/compose-with-tanstack-query
|
|
3
|
+
description: >
|
|
4
|
+
Server-side / async data flow with `@tanstack/preact-query`. Key the query on
|
|
5
|
+
the table state that drives the request (pagination + sort + filters), pass
|
|
6
|
+
`placeholderData: keepPreviousData` to avoid a "0 rows flash" between pages,
|
|
7
|
+
set `manualPagination` / `manualSorting` / `manualFiltering` for the slices
|
|
8
|
+
the server owns, supply `rowCount`, and let `table.set*` writes to external
|
|
9
|
+
atoms re-key the query. Routing keywords: preact-query, server pagination,
|
|
10
|
+
keepPreviousData, useQuery, manualPagination, rowCount, fetchData.
|
|
11
|
+
type: composition
|
|
12
|
+
library: tanstack-table
|
|
13
|
+
framework: preact
|
|
14
|
+
library_version: '9.0.0-alpha.47'
|
|
15
|
+
requires:
|
|
16
|
+
- preact/client-to-server
|
|
17
|
+
- pagination
|
|
18
|
+
- state-management
|
|
19
|
+
sources:
|
|
20
|
+
- TanStack/table:examples/preact/with-tanstack-query/src/main.tsx
|
|
21
|
+
- TanStack/table:examples/preact/with-tanstack-query/src/fetchData.ts
|
|
22
|
+
- TanStack/table:docs/framework/preact/guide/table-state.md
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
This skill is the @tanstack/preact-query recipe for server-side tables. Read `tanstack-table/preact/client-to-server` first for the manual-mode mechanics; this skill is the Preact Query-specific wiring on top.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @tanstack/preact-query @tanstack/preact-table @tanstack/preact-store
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## The Standard Recipe
|
|
34
|
+
|
|
35
|
+
Own the slices that drive the request with external atoms. Read them with `useSelector` so the `queryKey` is reactive. Pass them through `options.atoms`. Set `manual*` for the slices the server owns. Use `placeholderData: keepPreviousData` so pagination doesn't flash empty.
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { useMemo, useReducer } from 'preact/hooks'
|
|
39
|
+
import { render } from 'preact'
|
|
40
|
+
import {
|
|
41
|
+
QueryClient,
|
|
42
|
+
QueryClientProvider,
|
|
43
|
+
keepPreviousData,
|
|
44
|
+
useQuery,
|
|
45
|
+
} from '@tanstack/preact-query'
|
|
46
|
+
import { useCreateAtom, useSelector } from '@tanstack/preact-store'
|
|
47
|
+
import {
|
|
48
|
+
createColumnHelper,
|
|
49
|
+
rowPaginationFeature,
|
|
50
|
+
tableFeatures,
|
|
51
|
+
useTable,
|
|
52
|
+
type PaginationState,
|
|
53
|
+
} from '@tanstack/preact-table'
|
|
54
|
+
import { fetchData } from './fetchData'
|
|
55
|
+
import type { Person } from './fetchData'
|
|
56
|
+
|
|
57
|
+
const queryClient = new QueryClient()
|
|
58
|
+
const _features = tableFeatures({ rowPaginationFeature })
|
|
59
|
+
const columnHelper = createColumnHelper<typeof _features, Person>()
|
|
60
|
+
|
|
61
|
+
const columns = columnHelper.columns([
|
|
62
|
+
columnHelper.accessor('firstName', {
|
|
63
|
+
header: 'First Name',
|
|
64
|
+
cell: (info) => info.getValue(),
|
|
65
|
+
}),
|
|
66
|
+
columnHelper.accessor('lastName', { header: 'Last Name' }),
|
|
67
|
+
columnHelper.accessor('age', { header: 'Age' }),
|
|
68
|
+
])
|
|
69
|
+
|
|
70
|
+
function App() {
|
|
71
|
+
const paginationAtom = useCreateAtom<PaginationState>({
|
|
72
|
+
pageIndex: 0,
|
|
73
|
+
pageSize: 10,
|
|
74
|
+
})
|
|
75
|
+
const pagination = useSelector(paginationAtom)
|
|
76
|
+
|
|
77
|
+
const dataQuery = useQuery({
|
|
78
|
+
queryKey: ['data', pagination],
|
|
79
|
+
queryFn: () => fetchData(pagination),
|
|
80
|
+
placeholderData: keepPreviousData, // no "0 rows" flash between pages
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const defaultData = useMemo(() => [], [])
|
|
84
|
+
|
|
85
|
+
const table = useTable(
|
|
86
|
+
{
|
|
87
|
+
_features,
|
|
88
|
+
_rowModels: {}, // server owns slicing
|
|
89
|
+
columns,
|
|
90
|
+
data: dataQuery.data?.rows ?? defaultData,
|
|
91
|
+
rowCount: dataQuery.data?.rowCount,
|
|
92
|
+
atoms: { pagination: paginationAtom },
|
|
93
|
+
manualPagination: true,
|
|
94
|
+
},
|
|
95
|
+
(state) => state,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
// table.nextPage() writes to paginationAtom → queryKey changes → refetch.
|
|
99
|
+
return null
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
render(
|
|
103
|
+
<QueryClientProvider client={queryClient}>
|
|
104
|
+
<App />
|
|
105
|
+
</QueryClientProvider>,
|
|
106
|
+
document.getElementById('root')!,
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Source: `examples/preact/with-tanstack-query/src/main.tsx`.
|
|
111
|
+
|
|
112
|
+
## Server `fetchData` Shape
|
|
113
|
+
|
|
114
|
+
The fetcher returns the page of rows plus the total row count so `table.getPageCount()` is correct.
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
// fetchData.ts
|
|
118
|
+
import type { PaginationState } from '@tanstack/preact-table'
|
|
119
|
+
|
|
120
|
+
export type Person = {
|
|
121
|
+
firstName: string
|
|
122
|
+
lastName: string
|
|
123
|
+
age: number /* … */
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export async function fetchData(pagination: PaginationState): Promise<{
|
|
127
|
+
rows: Person[]
|
|
128
|
+
rowCount: number
|
|
129
|
+
}> {
|
|
130
|
+
// server returns the current page and the total count
|
|
131
|
+
const res = await fetch(
|
|
132
|
+
`/api/people?page=${pagination.pageIndex}&size=${pagination.pageSize}`,
|
|
133
|
+
)
|
|
134
|
+
return res.json()
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Source: `examples/preact/with-tanstack-query/src/fetchData.ts`.
|
|
139
|
+
|
|
140
|
+
## Adding Sorting and Filters
|
|
141
|
+
|
|
142
|
+
Add more external atoms; include them in the `queryKey`; set the matching `manual*` flag. The server's fetcher accepts whatever shape you forward.
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
const _features = tableFeatures({
|
|
146
|
+
rowPaginationFeature,
|
|
147
|
+
rowSortingFeature,
|
|
148
|
+
columnFilteringFeature,
|
|
149
|
+
globalFilteringFeature,
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
const paginationAtom = useCreateAtom<PaginationState>({
|
|
153
|
+
pageIndex: 0,
|
|
154
|
+
pageSize: 10,
|
|
155
|
+
})
|
|
156
|
+
const sortingAtom = useCreateAtom<SortingState>([])
|
|
157
|
+
const columnFiltersAtom = useCreateAtom<ColumnFiltersState>([])
|
|
158
|
+
const globalFilterAtom = useCreateAtom<string>('')
|
|
159
|
+
|
|
160
|
+
const pagination = useSelector(paginationAtom)
|
|
161
|
+
const sorting = useSelector(sortingAtom)
|
|
162
|
+
const columnFilters = useSelector(columnFiltersAtom)
|
|
163
|
+
const globalFilter = useSelector(globalFilterAtom)
|
|
164
|
+
|
|
165
|
+
const dataQuery = useQuery({
|
|
166
|
+
queryKey: ['data', pagination, sorting, columnFilters, globalFilter],
|
|
167
|
+
queryFn: () =>
|
|
168
|
+
fetchData({ pagination, sorting, columnFilters, globalFilter }),
|
|
169
|
+
placeholderData: keepPreviousData,
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
const table = useTable({
|
|
173
|
+
_features,
|
|
174
|
+
_rowModels: {},
|
|
175
|
+
columns,
|
|
176
|
+
data: dataQuery.data?.rows ?? defaultData,
|
|
177
|
+
rowCount: dataQuery.data?.rowCount,
|
|
178
|
+
atoms: {
|
|
179
|
+
pagination: paginationAtom,
|
|
180
|
+
sorting: sortingAtom,
|
|
181
|
+
columnFilters: columnFiltersAtom,
|
|
182
|
+
globalFilter: globalFilterAtom,
|
|
183
|
+
},
|
|
184
|
+
manualPagination: true,
|
|
185
|
+
manualSorting: true,
|
|
186
|
+
manualFiltering: true,
|
|
187
|
+
})
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Common Mistakes
|
|
191
|
+
|
|
192
|
+
### CRITICAL `manualPagination` without `rowCount`
|
|
193
|
+
|
|
194
|
+
Wrong:
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
useTable({
|
|
198
|
+
/* … */,
|
|
199
|
+
data: dataQuery.data?.rows ?? defaultData,
|
|
200
|
+
manualPagination: true,
|
|
201
|
+
atoms: { pagination: paginationAtom },
|
|
202
|
+
// no rowCount
|
|
203
|
+
})
|
|
204
|
+
table.getPageCount() // Infinity
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Correct: always pass `rowCount: dataQuery.data?.rowCount`.
|
|
208
|
+
Source: `examples/preact/with-tanstack-query/src/main.tsx`.
|
|
209
|
+
|
|
210
|
+
### CRITICAL `queryKey` that doesn't include reactive table state
|
|
211
|
+
|
|
212
|
+
Wrong:
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
useQuery({
|
|
216
|
+
queryKey: ['data'],
|
|
217
|
+
queryFn: () => fetchData(pagination),
|
|
218
|
+
})
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Correct:
|
|
222
|
+
|
|
223
|
+
```tsx
|
|
224
|
+
useQuery({
|
|
225
|
+
queryKey: ['data', pagination /* + sorting, filters, etc. */],
|
|
226
|
+
queryFn: () => fetchData(pagination),
|
|
227
|
+
})
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The query cache must vary by the slice values, or you'll fetch once and never refresh on user interaction.
|
|
231
|
+
Source: `examples/preact/with-tanstack-query/src/main.tsx`.
|
|
232
|
+
|
|
233
|
+
### HIGH Missing `placeholderData: keepPreviousData`
|
|
234
|
+
|
|
235
|
+
Wrong: data goes `undefined` between pages; the table flashes empty.
|
|
236
|
+
|
|
237
|
+
Correct: include `placeholderData: keepPreviousData` so the table keeps the last page rendered until the new page resolves.
|
|
238
|
+
Source: `examples/preact/with-tanstack-query/src/main.tsx`.
|
|
239
|
+
|
|
240
|
+
### HIGH Inline `data: dataQuery.data?.rows ?? []`
|
|
241
|
+
|
|
242
|
+
Wrong:
|
|
243
|
+
|
|
244
|
+
```tsx
|
|
245
|
+
useTable({ /* … */, data: dataQuery.data?.rows ?? [] }) // new [] every render
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Correct:
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
const defaultData = useMemo(() => [], [])
|
|
252
|
+
useTable({ /* … */, data: dataQuery.data?.rows ?? defaultData })
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
A new empty array each render busts row-model memos.
|
|
256
|
+
|
|
257
|
+
### HIGH Keeping the client-side `_rowModels` when manual
|
|
258
|
+
|
|
259
|
+
Wrong:
|
|
260
|
+
|
|
261
|
+
```tsx
|
|
262
|
+
useTable({
|
|
263
|
+
_features,
|
|
264
|
+
_rowModels: { paginatedRowModel: createPaginatedRowModel() }, // wasted work
|
|
265
|
+
/* … */,
|
|
266
|
+
manualPagination: true,
|
|
267
|
+
})
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Correct: drop the row-model factory whose stage the server owns. With `manualPagination: true`, the server returns the page slice already.
|
|
271
|
+
|
|
272
|
+
### MEDIUM Creating a new `paginationAtom` per render
|
|
273
|
+
|
|
274
|
+
Wrong: `createAtom(...)` inside the component body.
|
|
275
|
+
|
|
276
|
+
Correct: `useCreateAtom(...)` (or atom at module scope).
|
|
277
|
+
Source: `examples/preact/basic-external-atoms/src/main.tsx`.
|
|
278
|
+
|
|
279
|
+
## See Also
|
|
280
|
+
|
|
281
|
+
- `tanstack-table/preact/client-to-server` — manual-mode mechanics independent of any specific async lib.
|
|
282
|
+
- `tanstack-table/preact/compose-with-tanstack-store` — slice atoms and sharing state.
|
|
283
|
+
- `tanstack-table/preact/production-readiness` — narrow selectors, stable refs.
|