From da110e17f8721456a72b8152b8a1915ee68a621c Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 1 Jul 2026 21:48:24 +0100 Subject: [PATCH 1/4] fix --- packages/solid-virtual/src/index.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/solid-virtual/src/index.tsx b/packages/solid-virtual/src/index.tsx index 9f16672a..61425d79 100644 --- a/packages/solid-virtual/src/index.tsx +++ b/packages/solid-virtual/src/index.tsx @@ -15,7 +15,7 @@ import { onCleanup, onMount, } from 'solid-js' -import { createStore, reconcile } from 'solid-js/store' +import { createStore } from 'solid-js/store' import type { PartialKeys, VirtualizerOptions } from '@tanstack/virtual-core' export * from '@tanstack/virtual-core' @@ -71,11 +71,7 @@ function createVirtualizerBase< sync: boolean, ) => { instance._willUpdate() - setVirtualItems( - reconcile(instance.getVirtualItems(), { - key: 'index', - }), - ) + setVirtualItems([...instance.getVirtualItems()]) setTotalSize(instance.getTotalSize()) options.onChange?.(instance, sync) }, From ec2fba8c29dd0323077fa7727056f7cb2b3e111d Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 1 Jul 2026 22:02:40 +0100 Subject: [PATCH 2/4] chore: use `getVirtualItems` directly --- packages/solid-virtual/src/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/solid-virtual/src/index.tsx b/packages/solid-virtual/src/index.tsx index 61425d79..088a9dfc 100644 --- a/packages/solid-virtual/src/index.tsx +++ b/packages/solid-virtual/src/index.tsx @@ -71,7 +71,7 @@ function createVirtualizerBase< sync: boolean, ) => { instance._willUpdate() - setVirtualItems([...instance.getVirtualItems()]) + setVirtualItems(instance.getVirtualItems()) setTotalSize(instance.getTotalSize()) options.onChange?.(instance, sync) }, From cf80b71cc32914e9e3ee79ac9dafdeb3c220f945 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 1 Jul 2026 22:18:46 +0100 Subject: [PATCH 3/4] tests: add tests for the bug fix --- .../solid-virtual/tests/filter-items.test.ts | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 packages/solid-virtual/tests/filter-items.test.ts diff --git a/packages/solid-virtual/tests/filter-items.test.ts b/packages/solid-virtual/tests/filter-items.test.ts new file mode 100644 index 00000000..2712c5f3 --- /dev/null +++ b/packages/solid-virtual/tests/filter-items.test.ts @@ -0,0 +1,51 @@ +import { expect, test } from 'vitest' +import { createRoot, createSignal } from 'solid-js' + +import { createVirtualizer } from '../src/index' + +test('virtual items correctly index into filtered data after reactive shrink', () => { + createRoot((dispose) => { + const all = ['apple', 'banana', 'cherry', 'date', 'fig', 'grape'] + const [query, setQuery] = createSignal('') + + const filtered = () => { + const q = query() + if (!q) return all + return all.filter(s => s.includes(q)) + } + + const virtualizer = createVirtualizer({ + get count() { + return filtered().length + }, + getScrollElement: () => null, + estimateSize: () => 60, + initialRect: { width: 800, height: 600 }, + }) + + expect(virtualizer.getVirtualItems().length).toBe(6) + expect(virtualizer.getVirtualItems().map(v => filtered()[v.index])) + .toEqual(['apple', 'banana', 'cherry', 'date', 'fig', 'grape']) + + setQuery('e') + + expect(virtualizer.getVirtualItems().length).toBe(4) + const items = virtualizer.getVirtualItems() + expect(items.map(v => filtered()[v.index])) + .toEqual(['apple', 'cherry', 'date', 'grape']) + + setQuery('zz') + + expect(virtualizer.getVirtualItems().length).toBe(0) + expect(virtualizer.getVirtualItems().map(v => filtered()[v.index])) + .toEqual([]) + + setQuery('cherry') + + expect(virtualizer.getVirtualItems().length).toBe(1) + expect(virtualizer.getVirtualItems().map(v => filtered()[v.index])) + .toEqual(['cherry']) + + dispose() + }) +}) From 3d3fbec67c5e26388032f80e222010bc440c9b0d Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 2 Jul 2026 01:03:46 +0100 Subject: [PATCH 4/4] tests(solid-virtual): add reactivity tests --- .../solid-virtual/tests/reactivity.test.ts | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 packages/solid-virtual/tests/reactivity.test.ts diff --git a/packages/solid-virtual/tests/reactivity.test.ts b/packages/solid-virtual/tests/reactivity.test.ts new file mode 100644 index 00000000..d202e5af --- /dev/null +++ b/packages/solid-virtual/tests/reactivity.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it } from 'vitest' +import { createRoot, createSignal } from 'solid-js' +import { unwrap } from 'solid-js/store' +import { createVirtualizer } from '../src/index' + +describe('reactivity: unaffected slots keep stable references', () => { + it('does not recreate VirtualItem objects for slots whose data did not change', () => { + createRoot((dispose) => { + const data = Array.from({ length: 50 }, (_, i) => `item-${i}`) + const [filtered, setFiltered] = createSignal(data) + + const virtualizer = createVirtualizer({ + get count() { + return filtered().length + }, + getScrollElement: () => document.createElement('div'), + estimateSize: () => 30, + overscan: 0, + }) + + const before = virtualizer.getVirtualItems() + const beforeRefs = before.map((item) => unwrap(item)) + + // Shrink the array; leaves the first visible rows' index/start/end/size untouched. + setFiltered(data.slice(0, 40)) + + const after = virtualizer.getVirtualItems() + const afterRefs = after.map((item) => unwrap(item)) + + const unaffectedCount = Math.min(beforeRefs.length, afterRefs.length) + for (let i = 0; i < unaffectedCount; i++) { + if ( + beforeRefs[i].start === afterRefs[i].start && + beforeRefs[i].end === afterRefs[i].end && + beforeRefs[i].index === afterRefs[i].index + ) { + expect(afterRefs[i]).toBe(beforeRefs[i]) + } + } + + dispose() + }) + }) +})