Skip to content

fix: improve row selection api performance#6344

Merged
KevinVandy merged 1 commit into
betafrom
row-selection-perf
Jun 25, 2026
Merged

fix: improve row selection api performance#6344
KevinVandy merged 1 commit into
betafrom
row-selection-perf

Conversation

@KevinVandy

@KevinVandy KevinVandy commented Jun 25, 2026

Copy link
Copy Markdown
Member

🎯 Changes

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm test:pr.

Summary by CodeRabbit

  • New Features
    • Added clearer row-selection behavior across the app, including support for tracking selected row IDs and improved “select all” handling.
  • Bug Fixes
    • Fixed “some rows selected” checks so they stay true when at least one row is selected, even if all rows are selected.
    • Improved indeterminate checkbox behavior so it clears correctly when everything is selected.
  • Documentation
    • Updated migration guides with the new row-selection behavior and recommended checkbox logic.

@nx-cloud

nx-cloud Bot commented Jun 25, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 227bf9c

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

☁️ Nx Cloud last updated this comment at 2026-06-25 04:01:29 UTC

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Row selection semantics now treat “some rows selected” as at least one selected row, add getSelectedRowIds(), thread deselectAll through toggle-all APIs, and update the row-selection tests, React example, and migration guides.

Changes

Row selection API update

Layer / File(s) Summary
Public contract and wiring
packages/table-core/src/features/row-selection/rowSelectionFeature.types.ts, packages/table-core/src/features/row-selection/rowSelectionFeature.ts
RowSelectionState is narrowed to selected-only entries, getSelectedRowIds() is added to Table_RowSelection, and toggle-all APIs forward the new opts.deselectAll argument.
Table selection logic
packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts
Table-level reset, toggle-all, selected-row model, and getIsSome*/getIsAll* logic now reads table.atoms.rowSelection, uses getSelectedRowIds(), and clears selection when deselectAll is set.
Row selection helpers
packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts
row_toggleSelected, mutateRowIsSelected, row_getIsSelected, selectRowsFn, isRowSelected, and isSubRowSelected now pass the explicit selection map through mutation, recursion, and descendant checks.
Tests
packages/table-core/tests/implementation/features/row-selection/rowSelectionFeature.test.ts
The row-selection tests update helper calls for the new signatures and add coverage for deselectAll and memoization of selection getters.
Docs and example updates
docs/framework/*/guide/migrating.md, examples/react/row-selection/src/main.tsx
Migration guides and the React row-selection example describe the revised “some rows selected” semantics and the conditional state preview.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • TanStack/table#6314: Overlaps in packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts, especially selectRowsFn and row-selection test updates.
  • TanStack/table#6338: Also changes row-selection utility behavior and selection-map handling in the same feature area.

Poem

A bunny hopped through rows so neat,
with selection crumbs and carrot feet.
When all were picked, the sigh went clear,
indeterminate faded from my ear.
Hop, hop — v9 feels sweet. 🐰

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change: performance improvements to the row selection API.
Docstring Coverage ✅ Passed Docstring coverage is 94.12% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch row-selection-perf

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 Jun 25, 2026

Copy link
Copy Markdown
More templates

@tanstack/angular-table

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

@tanstack/angular-table-devtools

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

@tanstack/lit-table

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

@tanstack/match-sorter-utils

npm i https://pkg.pr.new/TanStack/table/@tanstack/match-sorter-utils@6344

@tanstack/preact-table

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

@tanstack/preact-table-devtools

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

@tanstack/react-table

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

@tanstack/react-table-devtools

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

@tanstack/solid-table

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

@tanstack/solid-table-devtools

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

@tanstack/svelte-table

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

@tanstack/table-core

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

@tanstack/table-devtools

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

@tanstack/vue-table

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

@tanstack/vue-table-devtools

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

commit: 227bf9c

@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: 2

Caution

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

⚠️ Outside diff range comments (1)
packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts (1)

246-267: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use the matching row model for filtered/grouped selection.

Both helpers still feed table.getCoreRowModel() into selectRowsFn, so filtered selected models can include filtered-out rows and grouped selected models ignore the sorted/grouped pipeline even though their memo deps invalidate on those models.

🐛 Proposed fix
 export function table_getFilteredSelectedRowModel<
   TFeatures extends TableFeatures,
   TData extends RowData,
 >(table: Table_Internal<TFeatures, TData>) {
-  const rowModel = table.getCoreRowModel()
+  const rowModel = table.getFilteredRowModel()
 
   if (
     !callMemoOrStaticFn(
       table,
@@
 export function table_getGroupedSelectedRowModel<
   TFeatures extends TableFeatures,
   TData extends RowData,
 >(table: Table_Internal<TFeatures, TData>) {
-  const rowModel = table.getCoreRowModel()
+  const rowModel = table.getSortedRowModel()
 
   if (
     !callMemoOrStaticFn(
       table,

Also applies to: 280-300

🤖 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 `@packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts`
around lines 246 - 267, The selection helpers are still using
table.getCoreRowModel(), which bypasses the filtered/grouped pipeline and can
return stale or hidden rows. Update table_getFilteredSelectedRowModel and the
matching grouped-selection helper to pass the appropriate row model from the
table pipeline into selectRowsFn, using the same memo/dependency path already
used for filtering and grouping so the selected row model stays aligned with the
current table state.
🧹 Nitpick comments (1)
packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts (1)

363-379: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid intermediate arrays in page-selection predicates.

These APIs can be hot on large pages; filter(...).some(...) allocates where a single pass is enough.

⚡ Proposed refactor
 export function table_getIsAllPageRowsSelected<
   TFeatures extends TableFeatures,
   TData extends RowData,
 >(table: Table_Internal<TFeatures, TData>) {
-  const paginationFlatRows = table
-    .getPaginatedRowModel()
-    .flatRows.filter((row) => row_getCanSelect(row))
+  const paginationFlatRows = table.getPaginatedRowModel().flatRows
   const rowSelection = table.atoms.rowSelection?.get() ?? {}
 
-  let isAllPageRowsSelected = !!paginationFlatRows.length
-
-  if (
-    isAllPageRowsSelected &&
-    paginationFlatRows.some((row) => !isRowSelected(row, rowSelection))
-  ) {
-    isAllPageRowsSelected = false
+  let hasSelectableRows = false
+  for (const row of paginationFlatRows) {
+    if (!row_getCanSelect(row)) continue
+    hasSelectableRows = true
+    if (!isRowSelected(row, rowSelection)) return false
   }
 
-  return isAllPageRowsSelected
+  return hasSelectableRows
 }
@@
 export function table_getIsSomePageRowsSelected<
   TFeatures extends TableFeatures,
   TData extends RowData,
 >(table: Table_Internal<TFeatures, TData>) {
   return table
     .getPaginatedRowModel()
-    .flatRows.filter((row) => row_getCanSelect(row))
-    .some((row) => row_getIsSelected(row) || row_getIsSomeSelected(row))
+    .flatRows.some(
+      (row) =>
+        row_getCanSelect(row) &&
+        (row_getIsSelected(row) || row_getIsSomeSelected(row)),
+    )
 }

Also applies to: 416-419

🤖 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 `@packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts`
around lines 363 - 379, The page-selection predicate in
table_getIsAllPageRowsSelected is allocating an intermediate array via
getPaginatedRowModel().flatRows.filter(...), which should be replaced with a
single pass over the paginated rows; update the logic in
table_getIsAllPageRowsSelected and the related page-selection check referenced
by the comment (“Also applies to: 416-419”) so selection status is computed
inline without filter(...).some(...) or other extra allocations.
🤖 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/framework/angular/guide/migrating.md`:
- Around line 680-692: The row-selection migration note currently explains the
indeterminate checkbox pattern only for table-wide selection, but it also
documents getIsSomePageRowsSelected() and should include the matching
page-scoped predicate. Update the guidance in the Row Selection API Changes
section to add the page-level formula alongside
getIsSomeRowsSelected()/getIsAllRowsSelected(), using the same style as the
existing example so page-row select-all UIs use the correct check.

In `@packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts`:
- Around line 311-315: `table_getSelectedRowIds` is returning every key from the
row-selection state, including entries whose value is false, which can make
selected-row helpers report stale or migrated state as selected; update it to
filter by the same truthy check used by `isRowSelected` so only runtime-selected
ids are returned. Apply the same truthy-value filtering in the related
`getIsSomeRowsSelected` path as well, so both helpers stay consistent with
row-model builders and controlled state like { rowA: false } is ignored.

---

Outside diff comments:
In `@packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts`:
- Around line 246-267: The selection helpers are still using
table.getCoreRowModel(), which bypasses the filtered/grouped pipeline and can
return stale or hidden rows. Update table_getFilteredSelectedRowModel and the
matching grouped-selection helper to pass the appropriate row model from the
table pipeline into selectRowsFn, using the same memo/dependency path already
used for filtering and grouping so the selected row model stays aligned with the
current table state.

---

Nitpick comments:
In `@packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts`:
- Around line 363-379: The page-selection predicate in
table_getIsAllPageRowsSelected is allocating an intermediate array via
getPaginatedRowModel().flatRows.filter(...), which should be replaced with a
single pass over the paginated rows; update the logic in
table_getIsAllPageRowsSelected and the related page-selection check referenced
by the comment (“Also applies to: 416-419”) so selection status is computed
inline without filter(...).some(...) or other extra allocations.
🪄 Autofix (Beta)

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

Run ID: 6b8d857b-7d27-4804-b1cb-03482619b12c

📥 Commits

Reviewing files that changed from the base of the PR and between d1c84eb and 227bf9c.

📒 Files selected for processing (12)
  • docs/framework/angular/guide/migrating.md
  • docs/framework/lit/guide/migrating.md
  • docs/framework/preact/guide/migrating.md
  • docs/framework/react/guide/migrating.md
  • docs/framework/solid/guide/migrating.md
  • docs/framework/svelte/guide/migrating.md
  • docs/framework/vue/guide/migrating.md
  • examples/react/row-selection/src/main.tsx
  • packages/table-core/src/features/row-selection/rowSelectionFeature.ts
  • packages/table-core/src/features/row-selection/rowSelectionFeature.types.ts
  • packages/table-core/src/features/row-selection/rowSelectionFeature.utils.ts
  • packages/table-core/tests/implementation/features/row-selection/rowSelectionFeature.test.ts

Comment thread docs/framework/angular/guide/migrating.md
@KevinVandy KevinVandy merged commit f077ae7 into beta Jun 25, 2026
8 checks passed
@KevinVandy KevinVandy deleted the row-selection-perf branch June 25, 2026 04:06
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