fix: increase pool only after a few elements have been rendered#11196
fix: increase pool only after a few elements have been rendered#11196
Conversation
649d378 to
4350c6e
Compare
4350c6e to
dd6d33e
Compare
| it('should render the first cell once during initialization', () => { | ||
| expect(getFirstCellRenderCount()).to.equal(1); | ||
| it('should render the first cell no more than twice during initialization', () => { | ||
| expect(getFirstCellRenderCount()).to.lessThanOrEqual(2); |
There was a problem hiding this comment.
Hmm...does this test change kind of reveal a regression that renderer calls are doubled?
Should there be a (virtualizer) test that validates the fix for "it caused the virtualizer to create more virtual elements than needed"?
There was a problem hiding this comment.
Hmm...does this test change kind of reveal a regression that renderer calls are doubled?
Yes, it's a small regression caused by the way iron-list is implemented: it always re-renders all rows after the physical count is increased. With the proposed change, the first increase happens in itemsChanged and the second in the subsequent scrollToIndex. We can't avoid two increases because we need the sizes of the initial batch of elements to estimate how many more are needed to fill the viewport. Optimizing the second increase to not re-render the already measured elements would require more changes, and I didn't want to go there yet :)
Should there be a (virtualizer) test that validates the fix for "it caused the virtualizer to create more virtual elements than needed"?
Yeah, I think I could create a virtualizer test.
There was a problem hiding this comment.
Yeah, I think I could create a virtualizer test.
Improved the existing test to catch the issue.
| } | ||
|
|
||
| // Prevent element update while the scroll position is being restored | ||
| this.__preventElementUpdates = true; |
There was a problem hiding this comment.
I traced down the PR which added __preventElementUpdates flag and it's #2801. The change was needed to support infinite scrolling in Flow Grid.
The PR also included two tests but for some reason they no longer exist in virtualizer.test.js (!)
I adapted the test from the PR for the current virtualizer logic and it passes on main:
it('should not request a different set of items on size increase', async () => {
const updateElement = sinon.spy((el, index) => {
el.textContent = `item-${index}`;
});
init({ size: 100, updateElement });
// Scroll halfway down the list
updateElement.resetHistory();
virtualizer.scrollToIndex(50);
await aTimeout(100);
const updatedIndexes = updateElement.getCalls().map((call) => call.args[1]);
// Increase the size so it shouldn't affect the current viewport items
updateElement.resetHistory();
virtualizer.size = 200;
const postResizeUpdatedIndexes = updateElement.getCalls().map((call) => call.args[1]);
if (postResizeUpdatedIndexes.length > 0) {
expect(postResizeUpdatedIndexes).to.eql(updatedIndexes);
}
expect(postResizeUpdatedIndexes).not.to.include(0);
});But with the changes of this PR, virtualizer would again render elements at unexpected indexed on size increase and the test would fail.
There was a problem hiding this comment.
Thanks! I've updated the solution so that it now focuses on initial render only.
89955a9 to
f491d4a
Compare
312670d to
b7e073a
Compare
| it('should not request a different set of items on size increase', async () => { | ||
| const updateElement = sinon.spy((el, index) => { | ||
| el.textContent = `item-${index}`; | ||
| }); | ||
| init({ size: 100, updateElement }); | ||
|
|
||
| // Scroll halfway down the list | ||
| updateElement.resetHistory(); | ||
| virtualizer.scrollToIndex(50); | ||
| await aTimeout(100); | ||
| const updatedIndexes = updateElement.getCalls().map((call) => call.args[1]); | ||
|
|
||
| // Increase the size so it shouldn't affect the current viewport items | ||
| updateElement.resetHistory(); | ||
| virtualizer.size = 200; | ||
| const postResizeUpdatedIndexes = updateElement.getCalls().map((call) => call.args[1]); | ||
|
|
||
| if (postResizeUpdatedIndexes.length > 0) { | ||
| expect(postResizeUpdatedIndexes).to.eql(updatedIndexes); | ||
| } | ||
| expect(postResizeUpdatedIndexes).not.to.include(0); | ||
| }); |
There was a problem hiding this comment.
The test case mentioned in the other comment was used for quick local testing and isn't really polished. For example it has await aTimeout(100); which makes it flaky.
We should probably instead restore this newer version that got removed in https://github.com/vaadin/web-components/pull/7205/changes#diff-17e7a20b2bf83191f4deeb3c213c8d0023dd076e4adb3da00f3d78ad00dcb3cfL187
it('should not request item updates on size increase', () => {
const updateElement = sinon.spy((el, index) => {
el.textContent = `item-${index}`;
});
init({ size: 100, updateElement });
// Scroll halfway down the list
virtualizer.scrollToIndex(50);
virtualizer.flush();
updateElement.resetHistory();
// Increase the size so it shouldn't affect the current viewport items
virtualizer.size = 200;
virtualizer.flush();
expect(updateElement.called).to.be.false;
});b1ef04c to
25ed337
Compare
|
|
||
| // Try to restore the scroll position if the new size is larger than 0 | ||
| if (size > 0) { | ||
| if (size > 0 && fvi > 0) { |
There was a problem hiding this comment.
This causes a minor regression in that it may lose the scroll position when for example the grid's size increases
Before:
Kapture.2026-02-27.at.12.51.00.mp4
After:
Kapture.2026-02-27.at.12.52.31.mp4
There was a problem hiding this comment.
If we're only targeting the initial render, maybe we could use some condition other than fvi > 0, maybe isClientFull
There was a problem hiding this comment.
Right... I've added a check for fviOffsetBefore < 0, and this seems to work now
There was a problem hiding this comment.
I guess we only need to care about scrollTop? const shouldRestoreScrollPosition = this._scrollTop > 0;
1fd5cbe to
8f5ad8f
Compare
|
|
Hi @vursen and @vursen, when i performed cherry-pick to this commit to 24.9, i have encountered the following issue. Can you take a look and pick it manually? |
…) (#11220) Co-authored-by: Sergey Vinogradov <mr.vursen@gmail.com>
|
This ticket/PR has been released with Vaadin 25.1.0-beta2. |



Description
This PR limits the scope of the
__preventElementUpdatesflag during size updates to only when scroll restoration is needed (the scroll position is not at the top). Before, when a non-zero size was set for the first time, the virtualizer created an initial batch of elements, but since the flag prevented their content from rendering, their heights didn't reflect the final values and instead defaulted to the minimum height set via CSS. As a result, the virtualizer could overestimate how many more elements it needed to add to fill the remaining viewport.This change improves Aura performance in the grid benchmark by 15%, narrowing the gap with Lumo to just 9%:
+ mixed-rendertime-chrome: performance improved by 15%And surprisingly, it also has a positive impact on Lumo itself:
+ mixed-rendertime-chrome: performance improved by 10%