Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions docs/framework/angular/guide/table-state.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ A table instance has a few state surfaces:

- `table.baseAtoms` are the internal writable atoms created from the resolved initial state.
- `table.atoms` are readonly derived atoms exposed per registered state slice.
- `table.state` is a readonly flat proxy over the registered `table.atoms`, useful for full-state debug output.
- `table.store` is the underlying readonly flat TanStack Store. Prefer `table.atoms` or `table.state` in app code.
- `table.store` is the readonly flat TanStack Store derived by putting all of the registered `table.atoms` together.

The Angular adapter provides `angularReactivity(injector)` as the table's reactivity binding. Core readonly atoms are Angular `computed` values, writable atoms are Angular `signal` values, and subscriptions bridge through `toObservable(computed(...), { injector })`. `injectTable` reruns the options initializer when Angular signals read inside it change, then calls `table.setOptions`.

Expand Down Expand Up @@ -61,7 +60,7 @@ this.table.atoms.sorting.get()
// this.table.atoms.rowSelection // TypeScript error unless rowSelectionFeature is registered
```

If `features` does not include a feature, its state should not be available in `table.atoms`, `table.store.state`, `initialState`, `state`, or `atoms`.
If `features` does not include a feature, its state should not be available in `table.atoms`, `table.store.get()`, `initialState`, `state`, or `atoms`.

### Accessing Table State

Expand All @@ -81,11 +80,11 @@ const pagination = this.table.atoms.pagination.get()
const sorting = this.table.atoms.sorting.get()
```

Use `table.state` when you need the current flat state shape, such as debug JSON:
Use `table.store.get()` when you need the current flat state shape, such as debug JSON:

```ts
const tableState = this.table.state
const stateJson = JSON.stringify(this.table.state, null, 2)
const tableState = this.table.store.get()
const stateJson = JSON.stringify(this.table.store.get(), null, 2)
```

Atom reads are signal reads in Angular. If `this.table.atoms.pagination.get()` is used in a template expression, `computed(...)`, or `effect(...)`, Angular tracks it and updates when that atom changes.
Expand Down Expand Up @@ -116,11 +115,11 @@ readonly pagination = computed(
readonly pageIndex = computed(() => this.pagination().pageIndex)
```

You can also select from the flat state proxy if that is more convenient, but prefer direct atoms for narrow render reads.
You can also select from the flat store snapshot if that is more convenient, but prefer direct atoms for narrow render reads.

```ts
readonly pagination = computed(
() => this.table.state.pagination,
() => this.table.store.get().pagination,
{ equal: shallow },
)
```
Expand Down Expand Up @@ -243,7 +242,7 @@ readonly table = injectTable(() => ({
}))
```

The v8-style `onStateChange` option is no longer part of the v9 `injectTable` state model. v9 encourages keeping table state slices atomic and separated for performance.
Use the per-slice `on[State]Change` callbacks to keep controlled table state slices atomic and separated.

##### On State Change Callbacks

Expand Down
124 changes: 69 additions & 55 deletions docs/framework/solid/guide/table-state.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ A table instance has a few state surfaces:
- `table.baseAtoms` are the internal writable atoms created from the resolved initial state.
- `table.atoms` are readonly derived atoms exposed per registered state slice.
- `table.store` is a readonly flat TanStack Store derived by putting all of the registered `table.atoms` together.
- `table.state()` is Solid-only selected state. It is the accessor returned from the selector passed as the second argument to `createTable`.

The Solid adapter provides `solidReactivity(owner)` to the table's `coreReativityFeature`. Core readonly atoms are Solid `createMemo` values and core writable atoms are Solid `createSignal` values. Because atom `.get()` reads through Solid signals and memos, table APIs can be consumed inside Solid computations and update only the computations that read the relevant state.

Expand Down Expand Up @@ -64,7 +63,7 @@ table.atoms.sorting.get()
// table.atoms.rowSelection // TypeScript error unless rowSelectionFeature is registered
```

If `features` does not include a feature, its state should not be available in `table.atoms`, `table.store.state`, `table.state()`, `initialState`, `state`, or `atoms`.
If `features` does not include a feature, its state should not be available in `table.atoms`, `table.store.get()`, `initialState`, `state`, or `atoms`.

### Accessing Table State

Expand All @@ -73,9 +72,9 @@ There are two different questions when reading table state:
- Do you only need the current value?
- Or should a Solid computation update when that value changes?

Use a direct atom or store read for the current value. Use a selector, accessor, or `table.Subscribe` when you want Solid's fine-grained updates.
Use direct atom reads for slice values. Use `table.store.get()` for the current flat state snapshot. Because Solid table atoms are backed by Solid signals and memos, atom reads participate in Solid dependency tracking when they happen inside JSX, `createMemo(...)`, `createEffect(...)`, or `table.Subscribe`.

#### Reading State Without Subscribing
#### Reading State

The simplest and most performant way to read a current state value is to read the matching atom:

Expand All @@ -87,77 +86,92 @@ const sorting = table.atoms.sorting.get()
You can also read the current flat store snapshot:

```tsx
const tableState = table.store.state
const pagination = table.store.state.pagination
const tableState = table.store.get()
const pagination = table.store.get().pagination
```

These reads are current-value reads. They only participate in Solid dependency tracking when they are called inside a Solid reactive scope that tracks those reads. If the UI needs to stay reactive to table state changes, use `table.state()`, `table.Subscribe`, or even a `useSelector` hook from TanStack Store.
These reads are current-value reads. They only participate in Solid dependency tracking when they are called inside a Solid reactive scope that tracks those reads. Prefer `table.atoms.<slice>.get()` for narrow reactive reads. Use `table.store.get()` for full-state debug output or when a computation intentionally depends on the whole table state.

#### Reading Reactive State with createTable
#### Reading Reactive State with Solid

The second argument to `createTable` is a TanStack Store selector. The selected value is exposed as `table.state()`. The default selector selects all registered table state.
Use Solid's native primitives to derive reactive values from table atoms or the flat store snapshot.

```tsx
const table = createTable(
{
features,
rowModels: {
paginatedRowModel: createPaginatedRowModel(),
},
columns,
get data() {
return data()
},
const table = createTable({
features,
rowModels: {
paginatedRowModel: createPaginatedRowModel(),
},
(state) => ({
pagination: state.pagination,
}),
)
columns,
get data() {
return data()
},
})

table.state().pagination
const pagination = createMemo(() => table.atoms.pagination.get())
const pageIndex = createMemo(() => pagination().pageIndex)

const tableStateJson = createMemo(() =>
JSON.stringify(table.store.get(), null, 2),
)
```

You can use the selected state in `createMemo`, JSX, or other Solid computations. Those computations update when the selected state changes.
You can use atom reads directly in JSX too:

```tsx
<span>
Page {table.atoms.pagination.get().pageIndex + 1} of {table.getPageCount()}
</span>
```

#### Fine-grained Updates with table.Subscribe

Use `table.Subscribe` when you want a specific part of the Solid tree to subscribe to a selected table state value. The child function receives a Solid accessor.
Use `table.Subscribe` when you want a specific part of the Solid tree to create a reactive render boundary. Its child function receives `table.atoms`, and Solid tracks only the atom reads used inside that child.

Without a `source` prop, `table.Subscribe` subscribes to `table.store` and requires a selector. With a `source` prop, it can subscribe directly to one atom or store.
```tsx
<table.Subscribe>
{(atoms) => {
void atoms.columnFilters.get()
void atoms.globalFilter.get()
void atoms.pagination.get()

return (
<tbody>
<For each={table.getRowModel().rows}>
{(row) => <tr>{/* ... */}</tr>}
</For>
</tbody>
)
}}
</table.Subscribe>
```

```tsx
<table.Subscribe
selector={(state) => ({
columnFilters: state.columnFilters,
globalFilter: state.globalFilter,
pagination: state.pagination,
})}
>
{() => (
<table.Subscribe>
{(atoms) => (
<tbody>
<For each={table.getRowModel().rows}>
{(row) => <tr>{/* ... */}</tr>}
{(row) => {
const isSelected = () => atoms.rowSelection.get()[row.id]

return (
<tr>
<td>
<input
type="checkbox"
checked={!!isSelected()}
onChange={row.getToggleSelectedHandler()}
/>
</td>
</tr>
)
}}
</For>
</tbody>
)}
</table.Subscribe>
```

```tsx
<table.Subscribe
source={table.atoms.rowSelection}
selector={(rowSelection) => rowSelection[row.id]}
>
{(isSelected) => (
<input
type="checkbox"
checked={!!isSelected()}
onChange={row.getToggleSelectedHandler()}
/>
)}
</table.Subscribe>
```

### Setting Table State

You should almost never need to set table state directly. TanStack Table features expose dedicated APIs for interacting with their state, and those APIs are the safest way to make changes.
Expand Down Expand Up @@ -232,11 +246,11 @@ Slice reset APIs like `resetPagination()` update through that feature's state up

### Controlled State

If you need easy access to table state in other parts of your application, you can control individual state slices. In v9, external atoms are the recommended way to do this because they preserve the atomic state model and Solid can update computations that read only the relevant slices.
If you need easy access to table state in other parts of your application, you can control individual state slices. In Solid, use native signals with `state` plus `on[State]Change` when you want Solid to own the slice. Use external TanStack Store atoms when you already want app-level atom sharing or direct atom subscriptions outside the table.

#### External Atoms

Use external atoms when the app should own one or more table state slices. Create stable writable atoms with `createAtom`, pass them to `atoms`, and subscribe to them with `useSelector` anywhere else in your app.
Use external atoms when the app should own one or more table state slices as TanStack Store atoms. Create stable writable atoms with `createAtom`, pass them to `atoms`, and subscribe to them with `useSelector` anywhere else in your app. `@tanstack/solid-store` is only needed by your app if you choose this pattern; the Solid table adapter itself uses Solid-native reactivity.

```tsx
import { createAtom, useSelector } from '@tanstack/solid-store'
Expand Down Expand Up @@ -284,7 +298,7 @@ When using the `atoms` option for a slice, you do not need to add the matching `

#### External State

The classic `state` plus `on[State]Change` pattern is still supported. This can be convenient for simple integrations or when migrating v8 code, but it is less atomic than external atoms.
Use `state` plus `on[State]Change` when Solid signals should own a table state slice.

```tsx
const [sorting, setSorting] = createSignal<SortingState>([])
Expand Down Expand Up @@ -316,7 +330,7 @@ const table = createTable({
})
```

The v8-style `onStateChange` option is no longer part of the v9 `createTable` state model. v9 encourages keeping table state slices atomic and separated for performance.
Use the per-slice `on[State]Change` callbacks to keep controlled table state slices atomic and separated.

##### On State Change Callbacks

Expand Down
Loading
Loading