Skip to content

fix: performance improvements related to cell order#6389

Merged
KevinVandy merged 1 commit into
betafrom
perf-ordering
Jul 7, 2026
Merged

fix: performance improvements related to cell order#6389
KevinVandy merged 1 commit into
betafrom
perf-ordering

Conversation

@KevinVandy

@KevinVandy KevinVandy commented Jul 7, 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

  • Performance Improvements

    • Column ordering, row cell access, and pinned-column views are now faster, with less repeated work during table updates.
    • Center-column views return sooner when nothing is pinned, improving responsiveness.
  • Bug Fixes

    • Column positions now stay accurate after reordering.
    • Pinned-column behavior is more reliable, including grouped columns and cases where a column appears in multiple pin regions.
    • Row cell reuse now preserves identity more consistently across column changes.

@nx-cloud

nx-cloud Bot commented Jul 7, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit bc0f71a

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

☁️ Nx Cloud last updated this comment at 2026-07-07 14:53:11 UTC

@pkg-pr-new

pkg-pr-new Bot commented Jul 7, 2026

Copy link
Copy Markdown
More templates

@tanstack/alpine-table

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

@tanstack/angular-table

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

@tanstack/angular-table-devtools

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

@tanstack/lit-table

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

@tanstack/match-sorter-utils

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

@tanstack/preact-table

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

@tanstack/preact-table-devtools

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

@tanstack/react-table

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

@tanstack/react-table-devtools

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

@tanstack/solid-table

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

@tanstack/solid-table-devtools

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

@tanstack/svelte-table

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

@tanstack/table-core

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

@tanstack/table-devtools

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

@tanstack/vue-table

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

@tanstack/vue-table-devtools

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

commit: bc0f71a

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR optimizes table-core hot paths: row cells are now cached per-row via a WeakMap to avoid reconstruction, column index lookups use a new memoized table_getColumnIndexes instead of linear scans, and column pinning/center-partition helpers add fast paths for empty pin state. Tests and perf-tracking docs are updated accordingly.

Changes

Performance optimizations

Layer / File(s) Summary
Row cell WeakMap caching
packages/table-core/src/core/rows/coreRowsFeature.types.ts, coreRowsFeature.utils.ts, src/utils.ts, tests/unit/core/rows/coreRowsFeature.utils.test.ts
Row_CoreProperties gains an optional _cellsCache WeakMap; row_getAllCells reuses cached cells keyed by column instance; copyInstancePropertiesWithoutMemos excludes _cellsCache; tests verify cell identity reuse and reordering behavior.
Memoized column index lookups
columnOrderingFeature.ts, columnOrderingFeature.types.ts, columnOrderingFeature.utils.ts, tests/unit/features/column-ordering/*
Adds ColumnIndexes type and table_getColumnIndexes table API that memoizes id→index maps per region; column_getIndex now does O(1) lookups instead of findIndex; tests cover index computation and reordering.
Column pinning fast paths
columnPinningFeature.utils.ts, tests/unit/features/column-pinning/*
column_getIsPinned iterates leaf columns directly for left/right precedence; center partition helpers short-circuit and return shared references when no columns are pinned; tests validate precedence and reference equality.
Perf tracking documentation
perf-done.md, perf-skipped.md, perf-todo.md
Catalog counts and entries updated to reflect completed, skipped, and remaining performance findings.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant row_getAllCells
  participant CellsCache
  participant constructCell
  Caller->>row_getAllCells: getAllCells()
  row_getAllCells->>CellsCache: lookup column in _cellsCache
  alt cell cached
    CellsCache-->>row_getAllCells: return cached Cell
  else cell missing
    row_getAllCells->>constructCell: constructCell(column)
    constructCell-->>row_getAllCells: new Cell
    row_getAllCells->>CellsCache: cache.set(column, cell)
  end
  row_getAllCells-->>Caller: cells array
Loading
sequenceDiagram
  participant Column
  participant column_getIndex
  participant table_getColumnIndexes
  participant Table
  Column->>column_getIndex: getIndex(position)
  column_getIndex->>table_getColumnIndexes: callMemoOrStaticFn(table)
  table_getColumnIndexes->>Table: read column order/pinning/visibility/grouping
  table_getColumnIndexes-->>column_getIndex: ColumnIndexes map
  column_getIndex-->>Column: indexes[region][column.id] ?? -1
Loading

Possibly related PRs

  • TanStack/table#6367: Modifies the same row_getAllCells cell caching (_cellsCache) and column-ordering index computation (table_getColumnIndexes/column_getIndex) logic.
🚥 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 is concise and broadly matches the PR’s main focus on performance improvements around cell and column ordering.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ 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 perf-ordering

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.

@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.

🧹 Nitpick comments (2)
packages/table-core/src/features/column-ordering/columnOrderingFeature.utils.ts (1)

40-63: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider building all four index maps in a single pass.

table_getColumnIndexes calls table_getPinnedVisibleLeafColumns four times (once per region) even though this memo is invalidated on every column-order/pinning/visibility change — exactly the hot path this PR targets (e.g. drag reordering). Per the column-pinning pipeline, the "all" list is already produced as [...left, ...center, ...right], so a single pass over table_getPinnedVisibleLeafColumns(table) combined with each column's pinned state could populate all, left, center, and right simultaneously, cutting the work from 4 scans to 1.

♻️ Sketch of a single-pass builder
 export function table_getColumnIndexes<...>(table: Table_Internal<TFeatures, TData>): ColumnIndexes {
-  const buildIndexes = (columns) => { ... }
-  return {
-    all: buildIndexes(table_getPinnedVisibleLeafColumns(table)),
-    center: buildIndexes(table_getPinnedVisibleLeafColumns(table, 'center')),
-    left: buildIndexes(table_getPinnedVisibleLeafColumns(table, 'left')),
-    right: buildIndexes(table_getPinnedVisibleLeafColumns(table, 'right')),
-  }
+  const all = makeObjectMap<number>()
+  const center = makeObjectMap<number>()
+  const left = makeObjectMap<number>()
+  const right = makeObjectMap<number>()
+  const columns = table_getPinnedVisibleLeafColumns(table)
+  let leftIdx = 0, centerIdx = 0, rightIdx = 0
+  for (let i = 0; i < columns.length; i++) {
+    const column = columns[i]!
+    all[column.id] = i
+    const pinned = column.getIsPinned()
+    if (pinned === 'left') left[column.id] = leftIdx++
+    else if (pinned === 'right') right[column.id] = rightIdx++
+    else center[column.id] = centerIdx++
+  }
+  return { all, center, left, right }
 }

This depends on the exact ordering contract of table_getPinnedVisibleLeafColumns(table) without a position argument (whether it truly returns left/center/right concatenated, matching each region's own filtered order) — worth confirming before applying.

🤖 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/column-ordering/columnOrderingFeature.utils.ts`
around lines 40 - 63, table_getColumnIndexes is doing four separate scans by
calling table_getPinnedVisibleLeafColumns for all/center/left/right, which is
wasteful on this hot path. Refactor table_getColumnIndexes to build the all,
center, left, and right index maps in one pass over the unfiltered pinned
visible leaf columns, using each column’s pinned region to assign into the
correct maps. Keep the existing ordering contract aligned with
table_getPinnedVisibleLeafColumns and preserve the current ColumnIndexes shape.
packages/table-core/src/features/column-pinning/columnPinningFeature.utils.ts (1)

142-157: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider Set for O(1) membership checks.

left.includes()/right.includes() inside the leaf-column loop is still O(leafColumns × pinCount) in the worst case (e.g. large grouped columns with many pinned entries). Since this PR's theme is eliminating linear scans (cf. the memoized table_getColumnIndexes used for column_getIndex elsewhere in the stack), converting left/right to Set before the loops would reduce membership checks to O(1) each.

♻️ Optional optimization using Set
   const { left, right } =
     column.table.atoms.columnPinning?.get() ?? getDefaultColumnPinningState()
 
+  const leftSet = new Set(left)
+  const rightSet = new Set(right)
+
   for (let i = 0; i < leafColumns.length; i++) {
-    if (left.includes(leafColumns[i]!.id)) {
+    if (leftSet.has(leafColumns[i]!.id)) {
       return 'left'
     }
   }
   for (let i = 0; i < leafColumns.length; i++) {
-    if (right.includes(leafColumns[i]!.id)) {
+    if (rightSet.has(leafColumns[i]!.id)) {
       return 'right'
     }
   }

For the common case of a single leaf column this is a wash, but it helps grouped columns with many leaves and/or large pin lists.

🤖 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/column-pinning/columnPinningFeature.utils.ts`
around lines 142 - 157, The pin-detection logic in column pinning still does
linear membership checks against left and right for every leaf column. Update
the getPinnedPosition logic in columnPinningFeature.utils.ts to convert the pin
arrays from columnPinning state into Set instances before iterating over
column.getLeafColumns(), then use constant-time membership checks when
determining whether a column is pinned left or right.
🤖 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.

Nitpick comments:
In
`@packages/table-core/src/features/column-ordering/columnOrderingFeature.utils.ts`:
- Around line 40-63: table_getColumnIndexes is doing four separate scans by
calling table_getPinnedVisibleLeafColumns for all/center/left/right, which is
wasteful on this hot path. Refactor table_getColumnIndexes to build the all,
center, left, and right index maps in one pass over the unfiltered pinned
visible leaf columns, using each column’s pinned region to assign into the
correct maps. Keep the existing ordering contract aligned with
table_getPinnedVisibleLeafColumns and preserve the current ColumnIndexes shape.

In
`@packages/table-core/src/features/column-pinning/columnPinningFeature.utils.ts`:
- Around line 142-157: The pin-detection logic in column pinning still does
linear membership checks against left and right for every leaf column. Update
the getPinnedPosition logic in columnPinningFeature.utils.ts to convert the pin
arrays from columnPinning state into Set instances before iterating over
column.getLeafColumns(), then use constant-time membership checks when
determining whether a column is pinned left or right.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 103e437d-9e26-4aaa-8e0b-fe91b6d6f470

📥 Commits

Reviewing files that changed from the base of the PR and between 965106a and bc0f71a.

📒 Files selected for processing (13)
  • packages/table-core/src/core/rows/coreRowsFeature.types.ts
  • packages/table-core/src/core/rows/coreRowsFeature.utils.ts
  • packages/table-core/src/features/column-ordering/columnOrderingFeature.ts
  • packages/table-core/src/features/column-ordering/columnOrderingFeature.types.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/utils.ts
  • packages/table-core/tests/unit/core/rows/coreRowsFeature.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
  • perf-done.md
  • perf-skipped.md
  • perf-todo.md

@KevinVandy KevinVandy merged commit 6a0facc into beta Jul 7, 2026
8 checks passed
@KevinVandy KevinVandy deleted the perf-ordering branch July 7, 2026 15:00
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