diff --git a/src/core/cache.spec.ts b/src/core/cache.spec.ts index 2f404491..3f14f83e 100644 --- a/src/core/cache.spec.ts +++ b/src/core/cache.spec.ts @@ -782,9 +782,52 @@ describe(initCache.name, () => { `); }); - it("should create cache from snapshot", () => { - expect(initCache(10, 23, [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 123])) - .toMatchInlineSnapshot(` + it("should restore cache from snapshot", () => { + const itemLength = 10; + const cache = initCache(itemLength, 23, [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + 123, + ]); + expect(cache).toMatchInlineSnapshot(` + { + "_computedOffsetIndex": -1, + "_defaultItemSize": 123, + "_length": 10, + "_offsets": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + ], + "_sizes": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + ], + } + `); + expect(cache._length).toBe(itemLength); + expect(cache._sizes.length).toBe(itemLength); + expect(cache._offsets.length).toBe(itemLength); + }); + + it("should restore cache from snapshot which has shorter length", () => { + const itemLength = 10; + const cache = initCache(itemLength, 23, [[0, 1, 2, 3, 4], 123]); + expect(cache).toMatchInlineSnapshot(` { "_computedOffsetIndex": -1, "_defaultItemSize": 123, @@ -807,14 +850,59 @@ describe(initCache.name, () => { 2, 3, 4, - 5, - 6, - 7, - 8, - 9, + -1, + -1, + -1, + -1, + -1, ], } `); + expect(cache._length).toBe(itemLength); + expect(cache._sizes.length).toBe(itemLength); + expect(cache._offsets.length).toBe(itemLength); + }); + + it("should restore cache from snapshot which has longer length", () => { + const itemLength = 10; + const cache = initCache(itemLength, 23, [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], + 123, + ]); + expect(cache).toMatchInlineSnapshot(` + { + "_computedOffsetIndex": -1, + "_defaultItemSize": 123, + "_length": 10, + "_offsets": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + ], + "_sizes": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + ], + } + `); + expect(cache._length).toBe(itemLength); + expect(cache._sizes.length).toBe(itemLength); + expect(cache._offsets.length).toBe(itemLength); }); }); diff --git a/src/core/cache.ts b/src/core/cache.ts index b4eee472..63d7ca22 100644 --- a/src/core/cache.ts +++ b/src/core/cache.ts @@ -165,11 +165,16 @@ export const initCache = ( itemSize: number, snapshot?: InternalCacheSnapshot ): Cache => { - const hasValidCache = - snapshot && snapshot[0] && snapshot[0].length === length; return { - _defaultItemSize: hasValidCache ? snapshot[1] : itemSize, - _sizes: hasValidCache ? snapshot[0] : fill([], length), + _defaultItemSize: snapshot ? snapshot[1] : itemSize, + _sizes: + snapshot && snapshot[0] + ? // https://github.com/inokawa/virtua/issues/441 + fill( + snapshot[0].slice(0, min(snapshot[0].length, length)), + max(0, length - snapshot[0].length) + ) + : fill([], length), _length: length, _computedOffsetIndex: -1, _offsets: fill([], length), diff --git a/src/react/Virtualizer.tsx b/src/react/Virtualizer.tsx index 6536ff08..e2a55158 100644 --- a/src/react/Virtualizer.tsx +++ b/src/react/Virtualizer.tsx @@ -114,8 +114,8 @@ export interface VirtualizerProps { horizontal?: boolean; /** * You can restore cache by passing a {@link CacheSnapshot} on mount. This is useful when you want to restore scroll position after navigation. The snapshot can be obtained from {@link VirtualizerHandle.cache}. - * - * **The length of items must be the same as when you take the cache, otherwise restoration will fail.** + * + * **The length of items should be the same as when you take the cache, otherwise restoration may not work as expected.** */ cache?: CacheSnapshot; /** diff --git a/src/react/WindowVirtualizer.tsx b/src/react/WindowVirtualizer.tsx index 8213484c..3d219e85 100644 --- a/src/react/WindowVirtualizer.tsx +++ b/src/react/WindowVirtualizer.tsx @@ -74,7 +74,7 @@ export interface WindowVirtualizerProps { /** * You can restore cache by passing a {@link CacheSnapshot} on mount. This is useful when you want to restore scroll position after navigation. The snapshot can be obtained from {@link WindowVirtualizerHandle.cache}. * - * **The length of items must be the same as when you take the cache, otherwise restoration will fail.** + * **The length of items should be the same as when you take the cache, otherwise restoration may not work as expected.** */ cache?: CacheSnapshot; /**