Skip to content

fix(virtual-core): remove incorrect elementsCache cleanup using getItemKey#1148

Merged
piecyk merged 2 commits intoTanStack:mainfrom
piecyk:fix/1147
Mar 16, 2026
Merged

fix(virtual-core): remove incorrect elementsCache cleanup using getItemKey#1148
piecyk merged 2 commits intoTanStack:mainfrom
piecyk:fix/1147

Conversation

@piecyk
Copy link
Collaborator

@piecyk piecyk commented Mar 16, 2026

🎯 Changes

Fix incorrect elementsCache cleanup logic introduced when handling disconnected nodes.

The code attempted to call:

this.elementsCache.delete(this.options.getItemKey(index))

However this is unnecessary and unsafe because the cache cleanup for disconnected nodes is already handled in measureElement(null), which iterates the elementsCache by reference and removes stale entries.

This PR removes the redundant getItemKey call and relies on the existing cleanup logic. The unobserve call remains unchanged.

closes #1147

✅ Checklist

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

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • Bug Fixes

    • Fixed stale-index behavior in virtualized lists so cache entries are preserved during item removal, preventing out-of-range errors.
  • Tests

    • Added an end-to-end regression test that reproduces and verifies removal workflows and ensures no errors surface.
  • New Features

    • Added a small interactive demo page to reproduce and observe the stale-index scenario locally.
  • Chores

    • Included release metadata for a patch.

@changeset-bot
Copy link

changeset-bot bot commented Mar 16, 2026

🦋 Changeset detected

Latest commit: 6e47f8e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 7 packages
Name Type
@tanstack/virtual-core Patch
@tanstack/angular-virtual Patch
@tanstack/lit-virtual Patch
@tanstack/react-virtual Patch
@tanstack/solid-virtual Patch
@tanstack/svelte-virtual Patch
@tanstack/vue-virtual Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented Mar 16, 2026

📝 Walkthrough

Walkthrough

This PR fixes a stale-index bug where ResizeObserver callbacks could call getItemKey with out-of-range indices for disconnected nodes, and adds an e2e regression app + Playwright test to reproduce and guard against the issue.

Changes

Cohort / File(s) Summary
Core Bug Fix
packages/virtual-core/src/index.ts
Removed elementsCache.delete() call from the ResizeObserver disconnect path to avoid calling getItemKey with stale/out-of-bounds indices.
E2E Test App
packages/react-virtual/e2e/app/stale-index/index.html, packages/react-virtual/e2e/app/stale-index/main.tsx
Added a small React app that reproduces the stale-index scenario: virtualized list, guarded getItemKey that throws on stale indices, and UI to remove items.
E2E Test Case
packages/react-virtual/e2e/app/test/stale-index.spec.ts
New Playwright test that loads the app, scrolls to render items, removes items in batches, and asserts counts and absence of errors.
Build Config
packages/react-virtual/e2e/app/vite.config.ts
Registered the new stale-index HTML entry in build.rollupOptions.input so the e2e app is included in the build.
Release Metadata
.changeset/quick-rings-happen.md
Added a changeset noting the patch that removes the incorrect elementsCache cleanup.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 In a burrow of code where indices creep,
A watcher misplaced made rabbits weep.
I nudged one line, let the cache remain,
Now ResizeObserver hops without pain. 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Title check ✅ Passed The PR title clearly and concisely describes the main change: removing incorrect elementsCache cleanup that uses getItemKey, which is the core fix addressing issue #1147.
Description check ✅ Passed The PR description is well-structured and follows the template with detailed explanation of the problem, solution rationale, checklist completion, changeset generation, and issue closure reference.
Linked Issues check ✅ Passed The PR directly addresses issue #1147 by removing the unsafe getItemKey call with stale indices, and includes a regression test (stale-index) that guards against this bug recurring.
Out of Scope Changes check ✅ Passed All changes are scoped to the fix: core library change removing the problematic cache cleanup, e2e test demonstrating the bug scenario, Vite config update for new test app, and changeset metadata.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

Tip

CodeRabbit can generate a title for your PR based on the changes.

Add @coderabbitai placeholder anywhere in the title of your PR and CodeRabbit will replace it with a title based on the changes in the PR. You can change the placeholder by changing the reviews.auto_title_placeholder setting.

@nx-cloud
Copy link

nx-cloud bot commented Mar 16, 2026

View your CI Pipeline Execution ↗ for commit 6e47f8e

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 29s View ↗
nx run-many --target=build --exclude=examples/** ✅ Succeeded 16s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-16 07:26:11 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 16, 2026

More templates

@tanstack/angular-virtual

npm i https://pkg.pr.new/@tanstack/angular-virtual@1148

@tanstack/lit-virtual

npm i https://pkg.pr.new/@tanstack/lit-virtual@1148

@tanstack/react-virtual

npm i https://pkg.pr.new/@tanstack/react-virtual@1148

@tanstack/solid-virtual

npm i https://pkg.pr.new/@tanstack/solid-virtual@1148

@tanstack/svelte-virtual

npm i https://pkg.pr.new/@tanstack/svelte-virtual@1148

@tanstack/virtual-core

npm i https://pkg.pr.new/@tanstack/virtual-core@1148

@tanstack/vue-virtual

npm i https://pkg.pr.new/@tanstack/vue-virtual@1148

commit: 6e47f8e

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/react-virtual/e2e/app/test/stale-index.spec.ts (1)

16-16: Fixed timeouts are acceptable here but could cause flakiness.

The waitForTimeout calls are a pragmatic solution for waiting on ResizeObserver callbacks, which don't have a deterministic signal. If tests become flaky in CI, consider increasing these values or using page.waitForFunction to poll for a stable state.

Also applies to: 25-25, 35-35

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-virtual/e2e/app/test/stale-index.spec.ts` at line 16, The
fixed short timeouts (await page.waitForTimeout(100)) in stale-index.spec.ts can
cause flakiness; replace these calls (all occurrences at the three locations)
with a polling wait that asserts the expected stable DOM/size state using
page.waitForFunction instead of a hard sleep: implement a page.waitForFunction
that reads the relevant element's computed size, index text, or a test-specific
attribute until it stabilizes (e.g., compare last and current boundingClientRect
or innerText over two consecutive ticks) and use that in place of the
waitForTimeout calls so ResizeObserver-driven updates are reliably awaited.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/react-virtual/e2e/app/test/stale-index.spec.ts`:
- Line 16: The fixed short timeouts (await page.waitForTimeout(100)) in
stale-index.spec.ts can cause flakiness; replace these calls (all occurrences at
the three locations) with a polling wait that asserts the expected stable
DOM/size state using page.waitForFunction instead of a hard sleep: implement a
page.waitForFunction that reads the relevant element's computed size, index
text, or a test-specific attribute until it stabilizes (e.g., compare last and
current boundingClientRect or innerText over two consecutive ticks) and use that
in place of the waitForTimeout calls so ResizeObserver-driven updates are
reliably awaited.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ead7d156-5397-42f4-8069-7e2e4507bce3

📥 Commits

Reviewing files that changed from the base of the PR and between c2f1c39 and 40053e0.

📒 Files selected for processing (5)
  • packages/react-virtual/e2e/app/stale-index/index.html
  • packages/react-virtual/e2e/app/stale-index/main.tsx
  • packages/react-virtual/e2e/app/test/stale-index.spec.ts
  • packages/react-virtual/e2e/app/vite.config.ts
  • packages/virtual-core/src/index.ts
💤 Files with no reviewable changes (1)
  • packages/virtual-core/src/index.ts

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
.changeset/quick-rings-happen.md (1)

1-5: Consider adding more context to the changeset description.

The changeset format is correct and the description matches the PR title. However, users reading the release notes might benefit from understanding the impact of this fix. Consider expanding the description to briefly mention that this resolves potential assertion failures or out-of-bounds errors when items are removed from a virtualized list.

📝 Example enhanced description
 ---
 '@tanstack/virtual-core': patch
 ---
 
-fix(virtual-core): remove incorrect elementsCache cleanup using getItemKey
+fix(virtual-core): remove incorrect elementsCache cleanup using getItemKey
+
+Fixes a bug where ResizeObserver could call getItemKey with stale indices for disconnected DOM nodes, potentially causing assertion failures or out-of-bounds access when items are removed from virtualized lists.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.changeset/quick-rings-happen.md around lines 1 - 5, Update the changeset
description for the '@tanstack/virtual-core' patch (the entry that says
"fix(virtual-core): remove incorrect elementsCache cleanup using getItemKey") to
include brief context about the impact: state that this fix prevents potential
assertion failures or out-of-bounds errors when items are removed from a
virtualized list and briefly note that it corrects elementsCache cleanup logic
that used getItemKey incorrectly so users understand why they should upgrade.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.changeset/quick-rings-happen.md:
- Around line 1-5: Update the changeset description for the
'@tanstack/virtual-core' patch (the entry that says "fix(virtual-core): remove
incorrect elementsCache cleanup using getItemKey") to include brief context
about the impact: state that this fix prevents potential assertion failures or
out-of-bounds errors when items are removed from a virtualized list and briefly
note that it corrects elementsCache cleanup logic that used getItemKey
incorrectly so users understand why they should upgrade.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4aeab723-607c-4595-9a19-0229bc899853

📥 Commits

Reviewing files that changed from the base of the PR and between 40053e0 and 6e47f8e.

📒 Files selected for processing (1)
  • .changeset/quick-rings-happen.md

@piecyk piecyk merged commit 7ece2d5 into TanStack:main Mar 16, 2026
6 checks passed
@github-actions github-actions bot mentioned this pull request Mar 16, 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.

ResizeObserver calls getItemKey with stale index for removed node

1 participant