Skip to content

Conversation

@johanrd
Copy link
Contributor

@johanrd johanrd commented Oct 4, 2025

Fix for #296 race condition with shouldRecycle=false during fast scroll.

The issue was that we had 'forgotten' to apply the fix 'Insert the virtual component bound back before removal to make sure Glimmer not confused about the state of the DOM.' also to the case shouldRecycle=true.

I was able to reproduce with scrolling fast up and down with the following component:

// @ts-expect-error VerticalCollection is not typed yet
import VerticalCollection from '@html-next/vertical-collection/components/vertical-collection/component'

const projectList = [{id:'dd3gd'},{id:'yd3u5'},{id:'3d34'},{id:'hd3zx'},{id:'3d3h'},{id:'hd3ss'},{id:'3d3z'},{id:'zd319'},{id:'id371'},{id:'9d33x'},{id:'9d3b1'},{id:'3d35'},{id:'zd38d'},{id:'id3l9'},{id:'wd3g1'},{id:'hd3st'},{id:'9d3p9'},{id:'9d3i5'},{id:'id3e5'},{id:'id3sd'},{id:'id3zh'},{id:'jd3kt'},{id:'9d3wd'},{id:'hd3zw'},{id:'jd36l'},{id:'jd3dp'},{id:'zd3fh'},{id:'id370'},{id:'ad33h'},{id:'jd3rx'},{id:'wd3n5'},{id:'dd3ng'},{id:'dd3uk'},{id:'ed31o'},{id:'ed38s'},{id:'yd30x'},{id:'3d3v'},{id:'zd3tp'},{id:'zd3ml'},{id:'jd3z1'},{id:'xd38h'},{id:'id3e4'},{id:'3d3w'},{id:'kd3d9'},{id:'kd3kd'},{id:'kd365'},{id:'0d37x'},{id:'0d3f1'},{id:'ad3al'},{id:'0d3m5'},{id:'0d30t'}];

<template>
  <nav id='project-menu-nav' style='flex: 1; overflow: auto; height: 100%'>
    <VerticalCollection
      @items={{projectList}}
      @containerSelector='#project-menu-nav'
      @estimateHeight={{121}}
      @staticHeight={{true}}
      @shouldRecycle={{false}}
      @key='id'
      as |project|
    >
      <div style='width: 100%; height: 121px; border-bottom: 1px solid lightgray; box-sizing: border-box;'>
        {{project.id}}
      </div>
    </VerticalCollection>
  </nav>
</template>

Here the scroll window was 350 px height, it is more reproducible when it is small, and scrolling up and down very quickly:

Screen.Recording.2025-10-03.at.22.55.31.mov

@johanrd johanrd force-pushed the fix/296-remove-component-pool branch 2 times, most recently from bcd90d0 to 0f591a2 Compare October 4, 2025 18:54
Consolidates component removal, prepend, and append operations into a
single layout callback to prevent concurrent DOM manipulation that caused
"removeChild" and "Cannot read properties of null" errors.

Fixes html-next#296
@johanrd johanrd changed the title Proposed fix for #296: remove component pool Fix for #296: remove component pool Oct 6, 2025
@johanrd johanrd changed the title Fix for #296: remove component pool Fix for #296: Insert the virtual component bound back to DOM also for the case shouldRecycle=true Oct 6, 2025
…the current runloop. If not, creates one."

This reverts commit ce6f221.
@johanrd
Copy link
Contributor Author

johanrd commented Oct 9, 2025

I think this should be quite uncontroversial. Pinging the ones I know have touched upon this issue before: @ahamedalthaf @Atrue @runspired @mixonic, as this seems to have had some history😅 please chip in if you see something concerning, or if you think it is ready for merge, thanks!

@johanrd johanrd changed the title Fix for #296: Insert the virtual component bound back to DOM also for the case shouldRecycle=true Fix for #296: Insert the virtual component bound back to DOM also for the case shouldRecycle=false Nov 3, 2025
for (let i = _componentPool.length - 1; i >= 0; i--) {
const component = _componentPool[i];
const item = objectAt(items, component.index);
if (shouldRecycle === true && item) {
Copy link
Contributor

Choose a reason for hiding this comment

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

On the last PR that worked on fixing this I'd left this comment for future follow up:

I think we should follow up with a check for whether we are in the document-fragment or still in the DOM, if we are not in the fragment there's no reason to re-insert in this way.

I suspect that might still be true but I'd need to sit down and reason through the code. The worst that could happen merging this as is I believe is that we just move some DOM "in-place" which isn't too big a deal.

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.

2 participants