Skip to content

refactor(table-core): centralize no-op state update guarding in setStateSlice - #6532

Merged
KevinVandy merged 14 commits into
mainfrom
centralize-noop-state-guard
Aug 9, 2026
Merged

refactor(table-core): centralize no-op state update guarding in setStateSlice#6532
KevinVandy merged 14 commits into
mainfrom
centralize-noop-state-guard

Conversation

@KevinVandy

@KevinVandy KevinVandy commented Aug 8, 2026

Copy link
Copy Markdown
Member

Summary

Supersedes #6519 (thanks @Faithfinder, the analysis and measurements there motivated this, and the commit is co-authored accordingly). Instead of adding a per-slice guard to table_resetExpanded, this centralizes no-op suppression in one shared util that every guarded state slice routes through; the handful of hot-path slices that deliberately skip guarding call their change handler directly and say why in a comment.

Guarded state slices now route through a single setStateSlice(table, key, updater) util that guards with the structural stateSlicesEqual compare by default (isEqual remains an optional override for custom feature slices that want a cheaper or semantic-aware policy). The slice's on<Slice>Change handler receives a guarded updater:

onChange((current) => {
  const next = functionalUpdate(updater, current)
  return isEqual(current, next) ? current : next
})

Equality is evaluated inside the state owner's container, against the owner's own current value, never against the table's potentially stale snapshot. A structural no-op returns the owner's existing reference, and suppression rides on the owner's identity bailout:

  • Uncontrolled slices: the default handler (makeStateUpdater) writes through the base atom, and @tanstack/store atoms skip propagation entirely when Object.is(old, next) holds. No notification, no re-render.
  • Controlled slices: React setState (and the equivalent identity-bailing primitives in Solid, Vue, Svelte 5, Angular, and Preact) resolve the guarded updater against their own latest queued value and bail on the preserved reference, so no re-render and no new data reference to re-trigger auto resets.

Because the original updater only ever runs inside the owner, it runs exactly once per application and same-tick queued updates stay composable. An earlier revision resolved updaters eagerly against the table's snapshot; that could wrongly suppress a real update when the controlled owner had newer queued state, so the final design never resolves outside the owner.

stateSlicesEqual is a new depth-capped (3 container levels) structural compare covering every stock slice shape: arrays of fresh objects (sorting, columnFilters), nested arrays (columnPinning), array-valued filter values, null-prototype maps vs plain objects, enumerable symbols, and sparse arrays. Non-plain values (dates, class instances) and deeper nesting compare by reference; a false negative just lets the update proceed, so the compare always fails safe.

Which slices are guarded

Guarded via setStateSlice (all column-count-bounded; covers all four auto-reset slices):

  • At the setter: sorting, columnFilters, grouping, columnOrder, columnPinning, rowPinning, columnVisibility, pagination
  • At the reset (the auto-reset path): expanded, cellSelection

Deliberately unguarded slices call their change handler directly (table.options.on<Slice>Change?.(updater)) rather than routing through setStateSlice, so the guarded-or-not decision is visible in the call-site idiom and hot paths pay no routing overhead. Each site carries its rationale in a comment:

  • rowSelection: no auto reset, and selection maps scale with row count; an O(n) compare on every gesture would be pure overhead
  • columnSizing / columnResizing: pointer-frequency transient writes during resize
  • globalFilter: a scalar, so the owner's Object.is bailout already provides value equality
  • expanded setter: the sentinel-aware toggles already guard membership, a full compare could hit large row-id maps, and no structural compare can know the true sentinel and a materialized row-id map are semantically interchangeable
  • cellSelection setter: pointer-driven with potentially large ranges; the drag-extend focus short-circuit is kept

This makes every guarded setter, reset, and auto reset loop-proof: the #6499-style cycle (data identity change → auto reset → onExpandedChange → controlled consumer re-render → new data reference → repeat) is broken for any state owner with identity-bailout semantics, which includes the table's own atoms and the state primitives of every first-party adapter framework.

Removed code

  • The scalar pageIndex/pageSize guards in table_resetPageIndex / table_resetPageSize (subsumed by the pagination guard; see behavior changes below)
  • The vestigial safeUpdater wrapper in table_setPagination

Kept deliberately: row_toggleExpanded's membership guard and table_toggleAllRowsExpanded's sentinel/emptiness guards. Without them, a no-op toggle under the expanded: true sentinel would materialize the sentinel into a row-id map, which is a real state change the central compare would (correctly) let through. The cell-selection drag-extend focus comparison is also kept as a cheap allocation-avoiding short-circuit.

Behavior changes

  • Suppression happens at the state-write level, not the handler-invocation level. A user-provided on<Slice>Change still runs for a structural no-op (only that handler's state container can know its latest queued value), but the guarded updater preserves the container's existing reference, so no state write, re-render, or downstream notification occurs. Handler bodies with unconditional side effects will still see those side effects run.
  • Pagination specifically: user-invoked table_resetPageIndex / table_resetPageSize no longer early-return before invoking the handler; the centralized guard turns the write into a no-op instead. The auto-reset path keeps a dedicated pre-check: table_autoResetPageIndex skips the reset entirely when already on the default page, so a user-provided onPaginationChange is never invoked for an auto-reset no-op. This preserves call-level suppression where it matters most, since auto resets fire on every data, filter, sort, and grouping change, and side-effectful handlers (for example a refetch under manualPagination with autoResetPageIndex: true) could otherwise self-sustain a refetch cycle. The check reads the pagination atom inside the scheduled, untracked auto-reset hook, so it registers no reactive dependencies.
  • Guarded slices always deliver a function-form updater to the handler, including for resets that previously delivered plain values. Updater<T> has always been value-or-function, so handlers that resolve with functionalUpdate are unaffected.
  • State updaters must be pure (they already had to be under React strict mode).
  • Loop protection relies on the owner's identity bailout. Containers that invalidate unconditionally on assignment (Glimmer @tracked, Svelte 4 writable stores) do not get the bail; the table's own atoms and all modern framework primitives do.

Testing

  • New tests/unit/setStateSlice.test.ts: compare semantics, no-op suppression for uncontrolled and controlled state, single execution of updaters, handler-invoked-but-reference-preserved semantics for controlled no-ops, the structural default guard and custom isEqual overrides, external-atom owners (ownership release composability), and clamped page navigation no-ops.
  • New packages/react-table/tests/expandedAutoResetLoop.test.tsx: the fix(table-core): wire expansion auto-reset into the core row model and guard first runs #6499/fix(table-core): don't fire onExpandedChange when resetExpanded is a no-op #6519 regression case with controlled expanded and an unstable data reference.
  • New table_autoResetPageIndex cases: the handler is not invoked when already on the default page, and still fires a real reset when off it.
  • Feature test suites that previously asserted handler firings for no-ops were updated to diverge state first (via controlled state/initialState) so they still cover their original semantics.
  • rowPinning tests updated for its newly guarded setter (handlers now receive function-form updaters, resolved via the shared getUpdaterResult helper).
  • Full monorepo test:lib, test:types, test:eslint, test:knip, and build (size-limit passes at 24.77 kB) pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added date-range filtering with minimum and maximum date inputs to filtering examples across supported frameworks.
    • Added birth-date columns demonstrating date filtering with inclusive bounds and clearable filters.
  • Bug Fixes
    • Prevented redundant state updates, callbacks, and re-renders when table state is unchanged.
    • Improved reset behavior for externally controlled table state.
    • Prevented expanded-row auto-reset loops.
  • Documentation
    • Documented new state-update utilities and refreshed API source references.
  • Tests
    • Expanded unit and end-to-end coverage for state updates, controlled state, pagination, selection, and date filtering.

…ateSlice

Route every table.setX state router through a single setStateSlice util
that resolves the updater once against the slice's current value,
structurally compares the result, and skips the onXChange handler
entirely when nothing changed. This removes the class of render loops
where auto resets fired change handlers with freshly allocated but
semantically identical values (e.g. autoResetExpanded after a data
reference change), and deletes the scattered ad-hoc guards that
previously protected individual slices.

For uncontrolled slices the default handler receives the pre-resolved
value, so updaters run exactly once. User handlers and externally owned
slices receive the original updater untouched to preserve functional
update composition and independent base-atom fallback semantics.

Supersedes #6519.

Co-Authored-By: Dmitrii Kartashev <dikartashev@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@nx-cloud

nx-cloud Bot commented Aug 8, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit fc79eaa

Command Status Duration Result
nx affected --targets=test:eslint,test:sherif,t... ✅ Succeeded 7m 44s View ↗
nx run-many --targets=build --exclude=examples/** ✅ Succeeded 59s View ↗

☁️ Nx Cloud last updated this comment at 2026-08-09 02:36:11 UTC

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

🚀 Changeset Version Preview

1 package(s) bumped directly, 17 bumped as dependents.

🟩 Patch bumps

Package Version Reason
@tanstack/table-core 9.1.1 → 9.1.2 Changeset
@tanstack/alpine-table 9.1.1 → 9.1.2 Dependent
@tanstack/angular-table 9.1.1 → 9.1.2 Dependent
@tanstack/angular-table-devtools 9.1.1 → 9.1.2 Dependent
@tanstack/ember-table 9.1.1 → 9.1.2 Dependent
@tanstack/lit-table 9.1.1 → 9.1.2 Dependent
@tanstack/match-sorter-utils 9.1.1 → 9.1.2 Dependent
@tanstack/octane-table 9.1.1 → 9.1.2 Dependent
@tanstack/preact-table 9.1.1 → 9.1.2 Dependent
@tanstack/preact-table-devtools 9.1.1 → 9.1.2 Dependent
@tanstack/react-table 9.1.1 → 9.1.2 Dependent
@tanstack/react-table-devtools 9.1.1 → 9.1.2 Dependent
@tanstack/solid-table 9.1.1 → 9.1.2 Dependent
@tanstack/solid-table-devtools 9.1.1 → 9.1.2 Dependent
@tanstack/svelte-table 9.1.1 → 9.1.2 Dependent
@tanstack/table-devtools 9.1.1 → 9.1.2 Dependent
@tanstack/vue-table 9.1.1 → 9.1.2 Dependent
@tanstack/vue-table-devtools 9.1.1 → 9.1.2 Dependent

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The pull request centralizes table state updates through setStateSlice, adds structural no-op detection, updates controlled-state tests and documentation, and adds birth-date range filtering examples with end-to-end coverage across frameworks.

Changes

Centralized state no-op handling

Layer / File(s) Summary
State comparison and update coordination
packages/table-core/src/utils.ts, packages/table-core/tests/unit/setStateSlice.test.ts
Adds bounded structural comparison and centralized controlled/uncontrolled state update handling.
Feature setter migration
packages/table-core/src/features/*/*Feature.utils.ts, packages/table-core/tests/unit/features/*/*Feature.utils.test.ts
Routes feature setters, resets, pagination, expansion, sorting, and cell selection through setStateSlice. Row selection remains exempt from structural no-op suppression.
State API documentation
docs/reference/index/**, .changeset/central-noop-state-guard.md
Documents the new state utilities and updates generated source links.
Auto-reset regression coverage
packages/react-table/tests/expandedAutoResetLoop.test.tsx
Tests controlled expanded-state resets with stable and unstable data references.
Build threshold
package.json
Raises the table-core distribution size limit to 30 KB.

Date-range filter examples

Layer / File(s) Summary
Cross-framework date-range controls
examples/*/filters/src/**, examples/alpine/filters/index.html
Adds generated birth dates, inDateRange registration, birth-date columns, and paired minimum and maximum date inputs.
Date-range end-to-end validation
examples/*/filters/tests/e2e/smoke.spec.ts
Tests date bounds, serialized filter state, filtered rows, clearing behavior, restored rows, and browser errors.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FilterInput
  participant TableFilter
  participant TableState
  participant RowModel
  FilterInput->>TableFilter: Set minimum or maximum birth date
  TableFilter->>TableState: Update inDateRange filter bounds
  TableState->>RowModel: Apply date-range filter
  RowModel-->>TableFilter: Return filtered rows
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the centralization of no-op state-update guarding in setStateSlice.
Description check ✅ Passed The description clearly explains the motivation, implementation, behavior changes, exceptions, testing, and release impact.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch centralize-noop-state-guard

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Aug 8, 2026

Copy link
Copy Markdown
More templates

@tanstack/alpine-table

npm i https://pkg.pr.new/@tanstack/alpine-table@6532

@tanstack/angular-table

npm i https://pkg.pr.new/@tanstack/angular-table@6532

@tanstack/angular-table-devtools

npm i https://pkg.pr.new/@tanstack/angular-table-devtools@6532

@tanstack/ember-table

npm i https://pkg.pr.new/@tanstack/ember-table@6532

@tanstack/lit-table

npm i https://pkg.pr.new/@tanstack/lit-table@6532

@tanstack/match-sorter-utils

npm i https://pkg.pr.new/@tanstack/match-sorter-utils@6532

@tanstack/octane-table

npm i https://pkg.pr.new/@tanstack/octane-table@6532

@tanstack/preact-table

npm i https://pkg.pr.new/@tanstack/preact-table@6532

@tanstack/preact-table-devtools

npm i https://pkg.pr.new/@tanstack/preact-table-devtools@6532

@tanstack/react-table

npm i https://pkg.pr.new/@tanstack/react-table@6532

@tanstack/react-table-devtools

npm i https://pkg.pr.new/@tanstack/react-table-devtools@6532

@tanstack/solid-table

npm i https://pkg.pr.new/@tanstack/solid-table@6532

@tanstack/solid-table-devtools

npm i https://pkg.pr.new/@tanstack/solid-table-devtools@6532

@tanstack/svelte-table

npm i https://pkg.pr.new/@tanstack/svelte-table@6532

@tanstack/table-core

npm i https://pkg.pr.new/@tanstack/table-core@6532

@tanstack/table-devtools

npm i https://pkg.pr.new/@tanstack/table-devtools@6532

@tanstack/vue-table

npm i https://pkg.pr.new/@tanstack/vue-table@6532

@tanstack/vue-table-devtools

npm i https://pkg.pr.new/@tanstack/vue-table-devtools@6532

commit: 0779dc4

nx-cloud[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/central-noop-state-guard.md:
- Line 11: Update the `onXChange` behavior statement in the changeset to remove
the incorrect `useState` identity-bail comparison and document that
`setStateSlice` suppresses events for structurally equal values, including newly
allocated equivalent objects.

In `@packages/table-core/src/utils.ts`:
- Around line 163-204: Update stateSlicesEqual’s array and object comparison
logic to distinguish sparse holes from explicitly stored undefined values by
checking ownership of each array index before recursive comparison. Include all
own enumerable keys, including symbol keys, when comparing object-like values
instead of relying only on Object.keys, while preserving the existing length,
prototype, and recursive equality checks.
- Around line 254-257: Update the state-update flow around functionalUpdate and
stateSlicesEqual to determine slice handler ownership before reading or
comparing the atom snapshot. Apply the local no-op guard only when the slice is
internally owned and uses the default state handler; for controlled or custom
handlers, delegate the original updater unchanged so queued host updates are
preserved. Add a test covering a queued controlled host update followed by a
locally apparent no-op.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a4cd23f3-7934-4442-b83a-4263144cf36d

📥 Commits

Reviewing files that changed from the base of the PR and between 058a520 and 3e49a33.

📒 Files selected for processing (34)
  • .changeset/central-noop-state-guard.md
  • package.json
  • packages/alpine-table/tests/unit/adapterCoverage.test.ts
  • packages/lit-table/tests/unit/adapterLifecycle.test.ts
  • packages/solid-table/tests/unit/adapterReactivity.test.ts
  • packages/table-core/src/features/cell-selection/cellSelectionFeature.utils.ts
  • packages/table-core/src/features/column-filtering/columnFilteringFeature.utils.ts
  • packages/table-core/src/features/column-grouping/columnGroupingFeature.utils.ts
  • packages/table-core/src/features/column-ordering/columnOrderingFeature.utils.ts
  • packages/table-core/src/features/column-pinning/columnPinningFeature.utils.ts
  • packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts
  • packages/table-core/src/features/column-sizing/columnSizingFeature.utils.ts
  • packages/table-core/src/features/column-visibility/columnVisibilityFeature.utils.ts
  • packages/table-core/src/features/global-filtering/globalFilteringFeature.utils.ts
  • packages/table-core/src/features/row-expanding/rowExpandingFeature.utils.ts
  • packages/table-core/src/features/row-pagination/rowPaginationFeature.utils.ts
  • packages/table-core/src/features/row-pinning/rowPinningFeature.utils.ts
  • packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts
  • packages/table-core/src/features/row-sorting/rowSortingFeature.utils.ts
  • packages/table-core/src/utils.ts
  • packages/table-core/tests/unit/features/column-filtering/columnFilteringFeature.utils.test.ts
  • packages/table-core/tests/unit/features/column-grouping/columnGroupingFeature.utils.test.ts
  • packages/table-core/tests/unit/features/column-ordering/columnOrderingFeature.utils.test.ts
  • packages/table-core/tests/unit/features/column-pinning/columnPinningFeature.utils.test.ts
  • packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts
  • packages/table-core/tests/unit/features/column-sizing/columnSizingFeature.utils.test.ts
  • packages/table-core/tests/unit/features/column-visibility/columnVisibilityFeature.utils.test.ts
  • packages/table-core/tests/unit/features/global-filtering/globalFilteringFeature.utils.test.ts
  • packages/table-core/tests/unit/features/row-expanding/rowExpandingFeature.utils.test.ts
  • packages/table-core/tests/unit/features/row-pagination/rowPaginationFeature.utils.test.ts
  • packages/table-core/tests/unit/features/row-pinning/rowPinningFeature.utils.test.ts
  • packages/table-core/tests/unit/features/row-selection/rowSelectionFeature.utils.test.ts
  • packages/table-core/tests/unit/features/row-sorting/rowSortingFeature.utils.test.ts
  • packages/table-core/tests/unit/setStateSlice.test.ts

Comment thread .changeset/central-noop-state-guard.md Outdated
Comment thread packages/table-core/src/utils.ts
Comment thread packages/table-core/src/utils.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/reference/index/functions/makeStateUpdater.md (1)

14-16: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Document the configured atom fallback.

makeStateUpdater first uses options.atoms[key] and falls back to baseAtoms[key]. The text at Lines 14-16 says that the updater always writes through the table base atom. Update it to describe both targets.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/reference/index/functions/makeStateUpdater.md` around lines 14 - 16,
Update the makeStateUpdater documentation to state that it writes through
options.atoms[key] when configured, otherwise falling back to baseAtoms[key],
while preserving the description of value and functional updater forms.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/reference/index/functions/setStateSlice.md`:
- Around line 72-87: Fix the generated Updater type reference in the
setStateSlice documentation so the indexed state lookup uses K directly rather
than the invalid K<K> expression. Regenerate the page or update the emitted
Markdown while preserving the existing state-key union and resulting indexed
state type.

---

Outside diff comments:
In `@docs/reference/index/functions/makeStateUpdater.md`:
- Around line 14-16: Update the makeStateUpdater documentation to state that it
writes through options.atoms[key] when configured, otherwise falling back to
baseAtoms[key], while preserving the description of value and functional updater
forms.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e5d91206-4edf-4039-8ff7-21f9e6bc4820

📥 Commits

Reviewing files that changed from the base of the PR and between 3e49a33 and bcfc0c8.

📒 Files selected for processing (162)
  • docs/reference/index/functions/assignPrototypeAPIs.md
  • docs/reference/index/functions/assignTableAPIs.md
  • docs/reference/index/functions/callMemoOrStaticFn.md
  • docs/reference/index/functions/flattenBy.md
  • docs/reference/index/functions/getFunctionNameInfo.md
  • docs/reference/index/functions/isFunction.md
  • docs/reference/index/functions/makeStateUpdater.md
  • docs/reference/index/functions/memo.md
  • docs/reference/index/functions/setStateSlice.md
  • docs/reference/index/functions/skipFirstRun.md
  • docs/reference/index/functions/stateSlicesEqual.md
  • docs/reference/index/functions/tableMemo.md
  • docs/reference/index/index.md
  • docs/reference/index/interfaces/API.md
  • docs/reference/index/interfaces/PrototypeAPI.md
  • docs/reference/index/type-aliases/APIObject.md
  • docs/reference/index/type-aliases/PrototypeAPIObject.md
  • docs/reference/static-functions/functions/cell_getCanSelect.md
  • docs/reference/static-functions/functions/cell_getIsFocused.md
  • docs/reference/static-functions/functions/cell_getIsSelected.md
  • docs/reference/static-functions/functions/cell_getSelectionEdges.md
  • docs/reference/static-functions/functions/cell_getSelectionExtendHandler.md
  • docs/reference/static-functions/functions/cell_getSelectionStartHandler.md
  • docs/reference/static-functions/functions/cell_getTabIndex.md
  • docs/reference/static-functions/functions/column_clearSorting.md
  • docs/reference/static-functions/functions/column_getAfter.md
  • docs/reference/static-functions/functions/column_getAutoFilterFn.md
  • docs/reference/static-functions/functions/column_getAutoSortDir.md
  • docs/reference/static-functions/functions/column_getAutoSortFn.md
  • docs/reference/static-functions/functions/column_getCanFilter.md
  • docs/reference/static-functions/functions/column_getCanHide.md
  • docs/reference/static-functions/functions/column_getCanMultiSort.md
  • docs/reference/static-functions/functions/column_getCanSort.md
  • docs/reference/static-functions/functions/column_getFilterFn.md
  • docs/reference/static-functions/functions/column_getFilterIndex.md
  • docs/reference/static-functions/functions/column_getFilterValue.md
  • docs/reference/static-functions/functions/column_getFirstSortDir.md
  • docs/reference/static-functions/functions/column_getIndex.md
  • docs/reference/static-functions/functions/column_getIsFiltered.md
  • docs/reference/static-functions/functions/column_getIsFirstColumn.md
  • docs/reference/static-functions/functions/column_getIsLastColumn.md
  • docs/reference/static-functions/functions/column_getIsSorted.md
  • docs/reference/static-functions/functions/column_getIsVisible.md
  • docs/reference/static-functions/functions/column_getNextSortingOrder.md
  • docs/reference/static-functions/functions/column_getSize.md
  • docs/reference/static-functions/functions/column_getSortFn.md
  • docs/reference/static-functions/functions/column_getSortIndex.md
  • docs/reference/static-functions/functions/column_getStart.md
  • docs/reference/static-functions/functions/column_getToggleSortingHandler.md
  • docs/reference/static-functions/functions/column_getToggleVisibilityHandler.md
  • docs/reference/static-functions/functions/column_resetSize.md
  • docs/reference/static-functions/functions/column_setFilterValue.md
  • docs/reference/static-functions/functions/column_toggleSorting.md
  • docs/reference/static-functions/functions/column_toggleVisibility.md
  • docs/reference/static-functions/functions/getDefaultCellSelectionState.md
  • docs/reference/static-functions/functions/getDefaultColumnFiltersState.md
  • docs/reference/static-functions/functions/getDefaultColumnOrderState.md
  • docs/reference/static-functions/functions/getDefaultColumnSizingColumnDef.md
  • docs/reference/static-functions/functions/getDefaultColumnSizingState.md
  • docs/reference/static-functions/functions/getDefaultColumnVisibilityState.md
  • docs/reference/static-functions/functions/getDefaultRowSelectionState.md
  • docs/reference/static-functions/functions/header_getSize.md
  • docs/reference/static-functions/functions/header_getStart.md
  • docs/reference/static-functions/functions/isRowSelected.md
  • docs/reference/static-functions/functions/isSubRowSelected.md
  • docs/reference/static-functions/functions/orderColumns.md
  • docs/reference/static-functions/functions/row_getCanExpand.md
  • docs/reference/static-functions/functions/row_getCanMultiSelect.md
  • docs/reference/static-functions/functions/row_getCanSelect.md
  • docs/reference/static-functions/functions/row_getCanSelectSubRows.md
  • docs/reference/static-functions/functions/row_getIsAllParentsExpanded.md
  • docs/reference/static-functions/functions/row_getIsAllSubRowsSelected.md
  • docs/reference/static-functions/functions/row_getIsExpanded.md
  • docs/reference/static-functions/functions/row_getIsSelected.md
  • docs/reference/static-functions/functions/row_getIsSomeSelected.md
  • docs/reference/static-functions/functions/row_getToggleExpandedHandler.md
  • docs/reference/static-functions/functions/row_getToggleSelectedHandler.md
  • docs/reference/static-functions/functions/row_getVisibleCells.md
  • docs/reference/static-functions/functions/row_getVisibleCellsByColumnId.md
  • docs/reference/static-functions/functions/row_toggleExpanded.md
  • docs/reference/static-functions/functions/row_toggleSelected.md
  • docs/reference/static-functions/functions/selectRowsFn.md
  • docs/reference/static-functions/functions/shouldAutoRemoveFilter.md
  • docs/reference/static-functions/functions/table_autoResetCellSelection.md
  • docs/reference/static-functions/functions/table_autoResetSorting.md
  • docs/reference/static-functions/functions/table_extendCellSelection.md
  • docs/reference/static-functions/functions/table_firstPage.md
  • docs/reference/static-functions/functions/table_getCanLastPage.md
  • docs/reference/static-functions/functions/table_getCanNextPage.md
  • docs/reference/static-functions/functions/table_getCanPreviousPage.md
  • docs/reference/static-functions/functions/table_getCanSomeRowsExpand.md
  • docs/reference/static-functions/functions/table_getCellSelectionBounds.md
  • docs/reference/static-functions/functions/table_getCellSelectionColumnIds.md
  • docs/reference/static-functions/functions/table_getCellSelectionColumnIndexes.md
  • docs/reference/static-functions/functions/table_getCellSelectionMergeBounds.md
  • docs/reference/static-functions/functions/table_getCellSelectionRowIds.md
  • docs/reference/static-functions/functions/table_getCenterTotalSize.md
  • docs/reference/static-functions/functions/table_getColumnIndexes.md
  • docs/reference/static-functions/functions/table_getColumnOffsets.md
  • docs/reference/static-functions/functions/table_getEndTotalSize.md
  • docs/reference/static-functions/functions/table_getExpandedDepth.md
  • docs/reference/static-functions/functions/table_getFilteredSelectedRowModel.md
  • docs/reference/static-functions/functions/table_getFocusedCell.md
  • docs/reference/static-functions/functions/table_getGroupedSelectedRowModel.md
  • docs/reference/static-functions/functions/table_getIsAllColumnsVisible.md
  • docs/reference/static-functions/functions/table_getIsAllPageRowsSelected.md
  • docs/reference/static-functions/functions/table_getIsAllRowsExpanded.md
  • docs/reference/static-functions/functions/table_getIsAllRowsSelected.md
  • docs/reference/static-functions/functions/table_getIsSomeColumnsVisible.md
  • docs/reference/static-functions/functions/table_getIsSomePageRowsSelected.md
  • docs/reference/static-functions/functions/table_getIsSomeRowsExpanded.md
  • docs/reference/static-functions/functions/table_getIsSomeRowsSelected.md
  • docs/reference/static-functions/functions/table_getOrderColumnsFn.md
  • docs/reference/static-functions/functions/table_getPageCount.md
  • docs/reference/static-functions/functions/table_getPageOptions.md
  • docs/reference/static-functions/functions/table_getPreSelectedRowModel.md
  • docs/reference/static-functions/functions/table_getRowCount.md
  • docs/reference/static-functions/functions/table_getSelectedCellCount.md
  • docs/reference/static-functions/functions/table_getSelectedCellIds.md
  • docs/reference/static-functions/functions/table_getSelectedCellRangesData.md
  • docs/reference/static-functions/functions/table_getSelectedRowIds.md
  • docs/reference/static-functions/functions/table_getSelectedRowModel.md
  • docs/reference/static-functions/functions/table_getStartTotalSize.md
  • docs/reference/static-functions/functions/table_getToggleAllColumnsVisibilityHandler.md
  • docs/reference/static-functions/functions/table_getToggleAllPageRowsSelectedHandler.md
  • docs/reference/static-functions/functions/table_getToggleAllRowsExpandedHandler.md
  • docs/reference/static-functions/functions/table_getToggleAllRowsSelectedHandler.md
  • docs/reference/static-functions/functions/table_getTotalSize.md
  • docs/reference/static-functions/functions/table_getVisibleFlatColumns.md
  • docs/reference/static-functions/functions/table_getVisibleLeafColumns.md
  • docs/reference/static-functions/functions/table_lastPage.md
  • docs/reference/static-functions/functions/table_moveCellSelection.md
  • docs/reference/static-functions/functions/table_nextPage.md
  • docs/reference/static-functions/functions/table_previousPage.md
  • docs/reference/static-functions/functions/table_resetCellSelection.md
  • docs/reference/static-functions/functions/table_resetColumnFilters.md
  • docs/reference/static-functions/functions/table_resetColumnOrder.md
  • docs/reference/static-functions/functions/table_resetColumnSizing.md
  • docs/reference/static-functions/functions/table_resetColumnVisibility.md
  • docs/reference/static-functions/functions/table_resetExpanded.md
  • docs/reference/static-functions/functions/table_resetPageIndex.md
  • docs/reference/static-functions/functions/table_resetPageSize.md
  • docs/reference/static-functions/functions/table_resetPagination.md
  • docs/reference/static-functions/functions/table_resetRowSelection.md
  • docs/reference/static-functions/functions/table_resetSorting.md
  • docs/reference/static-functions/functions/table_selectAllCells.md
  • docs/reference/static-functions/functions/table_selectCellRange.md
  • docs/reference/static-functions/functions/table_setCellSelection.md
  • docs/reference/static-functions/functions/table_setColumnFilters.md
  • docs/reference/static-functions/functions/table_setColumnOrder.md
  • docs/reference/static-functions/functions/table_setColumnSizing.md
  • docs/reference/static-functions/functions/table_setColumnVisibility.md
  • docs/reference/static-functions/functions/table_setFocusedCell.md
  • docs/reference/static-functions/functions/table_setPageIndex.md
  • docs/reference/static-functions/functions/table_setPageSize.md
  • docs/reference/static-functions/functions/table_setPagination.md
  • docs/reference/static-functions/functions/table_setRowSelection.md
  • docs/reference/static-functions/functions/table_setSorting.md
  • docs/reference/static-functions/functions/table_toggleAllColumnsVisible.md
  • docs/reference/static-functions/functions/table_toggleAllPageRowsSelected.md
  • docs/reference/static-functions/functions/table_toggleAllRowsSelected.md
  • packages/table-core/src/utils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/table-core/src/utils.ts

Comment thread docs/reference/index/functions/setStateSlice.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/alpine/filters/src/makeData.ts`:
- Line 34: Update the row-generation logic in makeData.ts so age and birthDate
are derived from the same generated value rather than independently. Apply the
same change at examples/alpine/filters/src/makeData.ts:34,
examples/angular/filters/src/app/makeData.ts:34,
examples/octane/filters/src/makeData.ts:34,
examples/react/filters/src/makeData.ts:34, and
examples/solid/filters/src/makeData.ts:34, preserving the 18–65 age range and
ensuring each row’s age matches its birthDate.

In `@examples/preact/filters/tests/e2e/smoke.spec.ts`:
- Around line 309-320: Make the maximum-bound assertions discriminating in the
smoke-test range-filter cases: in
examples/preact/filters/tests/e2e/smoke.spec.ts lines 309-320,
examples/lit/filters/tests/e2e/smoke.spec.ts lines 155-166,
examples/octane/filters/tests/e2e/smoke.spec.ts lines 300-311, and
examples/vue/filters/tests/e2e/smoke.spec.ts lines 307-318, set maxDate to
minDate, assert the bounded result is non-empty, and require every rendered date
to equal minDate while preserving the existing filter-value assertion.

In `@examples/react/row-selection/src/main.tsx`:
- Around line 307-309: Remove the orphaned State label and its empty container
alongside the commented-out table.state preview in the main component, or
restore a lightweight state preview within that container; ensure no standalone
State text remains when the preview is disabled.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e098f036-0c5e-464d-86fa-a5cf6cb1a234

📥 Commits

Reviewing files that changed from the base of the PR and between bcfc0c8 and c00a5dd.

📒 Files selected for processing (36)
  • .changeset/central-noop-state-guard.md
  • examples/alpine/filters/index.html
  • examples/alpine/filters/src/main.ts
  • examples/alpine/filters/src/makeData.ts
  • examples/alpine/filters/tests/e2e/smoke.spec.ts
  • examples/angular/filters/src/app/app.ts
  • examples/angular/filters/src/app/makeData.ts
  • examples/angular/filters/src/app/table-filter/table-filter.ts
  • examples/angular/filters/tests/e2e/smoke.spec.ts
  • examples/ember/filters/app/templates/application.gts
  • examples/ember/filters/app/utils/make-data.ts
  • examples/ember/filters/tests/e2e/smoke.spec.ts
  • examples/lit/filters/src/main.ts
  • examples/lit/filters/src/makeData.ts
  • examples/lit/filters/tests/e2e/smoke.spec.ts
  • examples/octane/filters/src/main.tsrx
  • examples/octane/filters/src/makeData.ts
  • examples/octane/filters/tests/e2e/smoke.spec.ts
  • examples/preact/filters/src/main.tsx
  • examples/preact/filters/src/makeData.ts
  • examples/preact/filters/tests/e2e/smoke.spec.ts
  • examples/react/filters/src/main.tsx
  • examples/react/filters/src/makeData.ts
  • examples/react/filters/tests/e2e/smoke.spec.ts
  • examples/react/row-selection/src/main.tsx
  • examples/solid/filters/src/App.tsx
  • examples/solid/filters/src/ColumnFilter.tsx
  • examples/solid/filters/src/makeData.ts
  • examples/solid/filters/tests/e2e/smoke.spec.ts
  • examples/vue/filters/src/App.vue
  • examples/vue/filters/src/Filter.vue
  • examples/vue/filters/src/makeData.ts
  • examples/vue/filters/src/tableHelper.ts
  • examples/vue/filters/tests/e2e/smoke.spec.ts
  • packages/table-core/src/utils.ts
  • packages/table-core/tests/unit/setStateSlice.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • .changeset/central-noop-state-guard.md
  • packages/table-core/tests/unit/setStateSlice.test.ts

'complicated',
'single',
])[0],
birthDate: faker.date.birthdate({ min: 18, max: 65, mode: 'age' }),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep age consistent with birthDate.

Each generator assigns age and birthDate independently. A row can show an age below 18 with a birth date for a person aged 18–65. Generate one value from the other.

  • examples/alpine/filters/src/makeData.ts#L34-L34: Derive age from the generated birthDate, or derive birthDate from age.
  • examples/angular/filters/src/app/makeData.ts#L34-L34: Derive age from the generated birthDate, or derive birthDate from age.
  • examples/octane/filters/src/makeData.ts#L34-L34: Derive age from the generated birthDate, or derive birthDate from age.
  • examples/react/filters/src/makeData.ts#L34-L34: Derive age from the generated birthDate, or derive birthDate from age.
  • examples/solid/filters/src/makeData.ts#L34-L34: Derive age from the generated birthDate, or derive birthDate from age.
📍 Affects 5 files
  • examples/alpine/filters/src/makeData.ts#L34-L34 (this comment)
  • examples/angular/filters/src/app/makeData.ts#L34-L34
  • examples/octane/filters/src/makeData.ts#L34-L34
  • examples/react/filters/src/makeData.ts#L34-L34
  • examples/solid/filters/src/makeData.ts#L34-L34
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/alpine/filters/src/makeData.ts` at line 34, Update the
row-generation logic in makeData.ts so age and birthDate are derived from the
same generated value rather than independently. Apply the same change at
examples/alpine/filters/src/makeData.ts:34,
examples/angular/filters/src/app/makeData.ts:34,
examples/octane/filters/src/makeData.ts:34,
examples/react/filters/src/makeData.ts:34, and
examples/solid/filters/src/makeData.ts:34, preserving the 18–65 age range and
ensuring each row’s age matches its birthDate.

Comment on lines +309 to +320
// The largest visible date keeps the range ordered and non-empty.
const maxDate = dates[dates.length - 1]!
await dateRangeFilter(page, 'max').fill(maxDate)

await expectColumnFilters(page, [
{ id: 'birthDate', value: [minDate, maxDate] },
])

for (const date of await readBodyColumn(page, 'birthDate')) {
expect(date >= minDate).toBe(true)
expect(date <= maxDate).toBe(true)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the maximum-bound test discriminating.

The selected maxDate is the largest date from the original page. A broken maximum bound can pass when no rendered min-filtered row exceeds that value.

Use maxDate = minDate. Assert that the bounded result is non-empty and that every rendered date equals minDate. This verifies the inclusive maximum bound.

  • examples/preact/filters/tests/e2e/smoke.spec.ts#L309-L320: use minDate as maxDate and assert exact bounded dates.
  • examples/lit/filters/tests/e2e/smoke.spec.ts#L155-L166: use minDate as maxDate and assert exact bounded dates.
  • examples/octane/filters/tests/e2e/smoke.spec.ts#L300-L311: use minDate as maxDate and assert exact bounded dates.
  • examples/vue/filters/tests/e2e/smoke.spec.ts#L307-L318: use minDate as maxDate and assert exact bounded dates.
📍 Affects 4 files
  • examples/preact/filters/tests/e2e/smoke.spec.ts#L309-L320 (this comment)
  • examples/lit/filters/tests/e2e/smoke.spec.ts#L155-L166
  • examples/octane/filters/tests/e2e/smoke.spec.ts#L300-L311
  • examples/vue/filters/tests/e2e/smoke.spec.ts#L307-L318
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/preact/filters/tests/e2e/smoke.spec.ts` around lines 309 - 320, Make
the maximum-bound assertions discriminating in the smoke-test range-filter
cases: in examples/preact/filters/tests/e2e/smoke.spec.ts lines 309-320,
examples/lit/filters/tests/e2e/smoke.spec.ts lines 155-166,
examples/octane/filters/tests/e2e/smoke.spec.ts lines 300-311, and
examples/vue/filters/tests/e2e/smoke.spec.ts lines 307-318, set maxDate to
minDate, assert the bounded result is non-empty, and require every rendered date
to equal minDate while preserving the existing filter-value assertion.

Comment thread examples/react/row-selection/src/main.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts`:
- Around line 53-58: Document in the central table state API contract that
table_setRowSelection intentionally bypasses the setStateSlice no-op guard and
may invoke onRowSelectionChange for structurally unchanged selections. Preserve
the existing row-selection behavior and explain that this applies to user
updates and explicit resets.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: edb74594-79ef-435e-815a-a15d1fa64e33

📥 Commits

Reviewing files that changed from the base of the PR and between c00a5dd and c7d1f94.

📒 Files selected for processing (3)
  • .changeset/central-noop-state-guard.md
  • packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts
  • packages/table-core/tests/unit/features/row-selection/rowSelectionFeature.utils.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • .changeset/central-noop-state-guard.md

Comment thread packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts Outdated

@nx-cloud nx-cloud Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

At least one additional CI pipeline execution has run since the conclusion below was written and it may no longer be applicable.

Nx Cloud is proposing a fix for your failed CI:

We fix the test:types failure by adding as RowSource to the rowSource initial value in Alpine.reactive(), which narrows the inferred type from string to the expected 'all' | 'custom' | 'filtered' | 'page' | 'selected' union. Without this assertion, Alpine's reactive wrapper widens the literal to string, causing both TS2322 errors where { rowSource: string } fails assignability to AggregationTableMeta. This aligns the alpine example with the pattern used in the vanilla and lit aggregation examples.

Tip

We verified this fix by re-running tanstack-alpine-table-example-aggregation:test:types.

diff --git a/examples/alpine/aggregation/src/main.ts b/examples/alpine/aggregation/src/main.ts
index a495306a..d486d946 100644
--- a/examples/alpine/aggregation/src/main.ts
+++ b/examples/alpine/aggregation/src/main.ts
@@ -91,7 +91,7 @@ const columns = columnHelper.columns([
 Alpine.data('table', () => {
   const local = Alpine.reactive({
     data: makeData(10_000),
-    rowSource: 'filtered',
+    rowSource: 'filtered' as RowSource,
   })
   const table = createTable({
     features,

Apply fix via Nx Cloud  Reject fix via Nx Cloud


Or Apply changes locally with:

npx nx-cloud apply-locally v22x-f6cr

Apply fix locally with your editor ↗   View interactive diff ↗



🎓 Learn more about Self-Healing CI on nx.dev

KevinVandy and others added 5 commits August 8, 2026 16:51
…, call unguarded handlers directly

setStateSlice now always guards, with isEqual as an override hook for
custom feature slices. Slices that deliberately skip guarding
(rowSelection, columnSizing, columnResizing, globalFilter, and the
expanded/cellSelection setters) call their change handler directly
instead of routing through a no-op wrapper, keeping pointer-frequency
paths free of the routing overhead. rowPinning picks up the guard via
the new default, matching columnPinning.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@KevinVandy
KevinVandy merged commit ff43666 into main Aug 9, 2026
11 checks passed
@KevinVandy
KevinVandy deleted the centralize-noop-state-guard branch August 9, 2026 02:48
@github-actions github-actions Bot mentioned this pull request Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant