Repository navigation
Replies: 1 comment
|
There is no public call that removes one item's measured size. At the commit you link (virtual-core 3.19.10; 3.19.11 gives the same output), // Re-measure one rendered row from the DOM (counts as a normal re-measurement)
virtualizer.resizeItem(index, rowEl.offsetHeight)
// Return one item's layout to its estimate. Total size is right, but the key stays
// in itemSizeCache, so it is still treated as measured.
virtualizer.resizeItem(index, virtualizer.options.estimateSize(index))What does not work:
I tested this with The same results hold on Your proposal for Confidence: high for the 3.19.x behavior shown above. I did not test the anchoring behavior during scroll, only the size totals. |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
The use case
An item's content changes shape while its key stays the same: a group row expands or collapses, a message is edited, a card swaps its body. The size virtual-core has cached for that key is now wrong.
When the row is rendered, the ResizeObserver picks the change up. When it is not, the cached size stays in the layout until the row is rendered again, and since #1183 the default
measureElementanswers the mount-time measurement fromitemSizeCachewhen it has the key, so the stale size is also what the row is placed with until its ResizeObserver entry arrives. Meanwhile every offset below the item is off:scrollToIndexto a later row lands short or long until the item has been measured again, and when it is, the correction follows the re-measurement anchoring rule (only items entirely above the viewport, not while scrolling backward) rather than the first-measurement one, so it shows as a jump.What we want to say is "the size you have for this item is no longer valid": use
estimateSizefor it until it is measured, measure it now if it is rendered, and treat that measurement as a first one.What is possible today
resizeItem(index, size)sets a size, but there is no way to unset one.resizeItem(index, estimateSize(index))comes close, except the item still counts as measured: the anchoring rule above,takeSnapshot()persisting the estimate as a measurement, and the mount-time measurement returning it instead of reading the element.measure()forgets every item, which costs all the measurements of a long list to fix one item, and moves every row that was measured until they are all measured again.itemSizeCacheis a publicMap(since feat(virtual-core): skip sync DOM read in measureElement when cached size exists #1183), but writing to it does nothing by itself: the layout memo keys on the privateitemSizeCacheVersion(index.ts L485). Bumping that version through the private field is not enough either: the rebuild starts at the privatependingMin, so a write below an index thatresizeItemhas dirtied since the last read is still ignored:Changing the item's key works, but the key is usually also the framework's element key, so the row remounts and loses its state, a changed first or last key goes through the prepend and append handling in
setOptions, and the old size stays initemSizeCacheuntil the nextmeasure().We maintain a Blazor wrapper over virtual-core, and it currently does the private-field version of this. For an item that is not rendered it deletes the key from
itemSizeCacheand bumpsitemSizeCacheVersion; for a rendered row it reads the size off the element and applies it throughresizeItem; and for a size it has to keep under a key that is not at a known index while the item set is being replaced, it writesitemSizeCachedirectly and resetspendingMintoo. It works, but only because of how the internals happen to be ordered today.Proposal
A counterpart to
resizeItem, for example:itemSizeCache, and lowerspendingMinand bumps the version the wayresizeItemdoes, so the layout falls back toestimateSize(index)for it.resizeItemwould for the size change (measured size to estimate) when the item is above the viewport.resizeItem.Open questions:
resetItemSize(index), ormeasure()taking an optional index (or indexes), sincemeasure()already means "reset measurements".resizeItemand the rest of the API. By key also covers an item that is not at a known index right now, for example while the item set is being replaced; that is the case where our wrapper needs to keep or forget a size today, but it may be too specific to us.Related
A public way to read one item's current layout by index would help too.
getMeasurements()is private, and the publicmeasurementsCachefield lags until the memo runs (see #1361). react-virtual reads that field itself fordirectDomUpdates(react-virtual/src/index.tsx L240), and #1021 shows consumers reading it as well. Happy to split that into its own discussion.If the direction sounds right, I can open a PR.
All reactions