vue-virtual-scroller icon indicating copy to clipboard operation
vue-virtual-scroller copied to clipboard

Replacing all items using RecycleScroller

Open mcfarljw opened this issue 6 years ago • 19 comments
trafficstars

I'm trying to use this plugin with data that can be filtered, but when I set the items with a new array it causes strange things to happen. Things will get out of order and include blank gaps. I'm including a watered down example. If you click regenerate, eventually you'll see things happen as shown in the screenshot where there is a gap between item1 and item2.

Is there some other way to regenerate all items that I'm missing?

Screen Shot 2019-07-11 at 4 31 15 PM

Example: https://codesandbox.io/s/vuetify-with-vue-virtual-scroller-khl8d

mcfarljw avatar Jul 11 '19 20:07 mcfarljw

I've got it working when using v-if="items.length" on the recycler component, then settings items to [] which forces the everything to rerender and waiting for this.$nextTick, but that doesn't seem like the intended way of handling things.

Example: https://codesandbox.io/s/vuetify-with-vue-virtual-scroller-working-yv0rz

mcfarljw avatar Jul 11 '19 20:07 mcfarljw

@mcfarljw #226 I think also shares this same problem. Being unable to refresh the computed size property data on the lower components.

I had this issue in use case, to reuse a single dynamic scroller but change the inner view items with v-if on them. Problem being the data staying the same, but the sizing being different between views. As the data is not changing, no computed getters are being re-triggered.

The fix was to bind key to a property you control so you can toggle re-rendering the component, which forces it to update everything[The whole component]. I'm not sure if this is the best way or if there are better, but it does work for me.

I have modified your codesandbox below, if you open console you can watch how every button click of regenerate does match the random item amount.

https://codesandbox.io/embed/vuetify-with-vue-virtual-scroller-working-i1b25

KyKlersy avatar Jul 20 '19 15:07 KyKlersy

@mcfarljw #226 I think also shares this same problem. Being unable to refresh the computed size property data on the lower components.

I had this issue in use case, to reuse a single dynamic scroller but change the inner view items with v-if on them. Problem being the data staying the same, but the sizing being different between views. As the data is not changing, no computed getters are being re-triggered.

The fix was to bind key to a property you control so you can toggle re-rendering the component, which forces it to update everything[The whole component]. I'm not sure if this is the best way or if there are better, but it does work for me.

I have modified your codesandbox below, if you open console you can watch how every button click of regenerate does match the random item amount.

https://codesandbox.io/embed/vuetify-with-vue-virtual-scroller-working-i1b25

I also met this issue, I'm trying to fix this using your solution, but the solution is a bit tricky. Could you please help me understand this?

So basically you add a key to the recycle-scroller, and when the data changes, you give it a new key. I haven't read any source code about "Vue.js", but by my understand, the :key is used for computing a list changes, and update each item by specific key.

Is there any reason to add this? If add a key to a non-list(v-for) component, does it force it update or compute when the key changes?

Please help, thank you very much.

citrus327 avatar Jul 29 '19 09:07 citrus327

@phshy0607 Adding the key and toggling it, is 'hack' based on the fact vue uses the "key" prop binding for component identity in the vue virtual dom. Changing the key forces vue to recreate a new recycle-scroller node which it will then patch into the real dom.

It should be considered a last resort to get behavior you are attempting. Using it like this should be scrutinized greatly if you start running into performance impact as this creates a new recycle-scroller component that then has to rebuild its computed sizes cache for your list data.

KyKlersy avatar Jul 29 '19 14:07 KyKlersy

@phshy0607 Adding the key and toggling it, is 'hack' based on the fact vue uses the "key" prop binding for component identity in the vue virtual dom. Changing the key forces vue to recreate a new recycle-scroller node which it will then patch into the real dom.

It should be considered a last resort to get behavior you are attempting. Using it like this should be scrutinized greatly if you start running into performance impact as this creates a new recycle-scroller component that then has to rebuild its computed sizes cache for your list data.

Thank you very much. This is very well explained, and thank you for the solution, it works for now.

citrus327 avatar Jul 29 '19 14:07 citrus327

I have pretty much the same problem. And I may have a theory where this comes from. I have a list of files I render in a sidebar. Everything is perfect, but obviously the user can change the name of these files. The files have an ID, which in my case is a simple hash of the absolute file path. So on rename, this file path changes, and so does the hash a.k.a. the ID.

If then I re-order the items (because one can sort the directory differently, obviously), the bug appears with the renamed file actually appearing twice, even though the hash changed, which means that the respective component is being re-used and then rendered on the original position (not the new one in the different sorting).

Here's how it looks:

image

As you can see, the file list itself only contains the file once, but it is being rendered twice. Completely removing the fileList and using a timeout to re-fill the list after a DOM rendering reflow fixes this, but obviously with a display glitch as the empty list is being rendered for a fraction of a second.

So it seems that the bug originates in the components not being re-validated. It doesn't check whether or not the respective hash/keyField exists more than once.

EDIT: I should add that another file is below the duplicate renamed file, so the duplicate file actually perfectly covers the file that's supposed to show up at the old position.

nathanlesage avatar Aug 04 '19 18:08 nathanlesage

when there are thounds of data, and scrolling very fast with the scrollbar, this problem reappears easily. image

Demo: https://codesandbox.io/s/vue-virtual-selector-60sds?file=/src/App.vue

skayi avatar Jun 23 '21 10:06 skayi

Sorry to revival this topic but i found work around.

If you use a v-show in the parent of dynamicscroll the spaces are recalculated:

<div v-show="showListTik">
    <DynamicScroller
        :items="galleryItems"
        .
        .
        .
.
.
.

get galleryItems():[] {
    const elements:[] = [......]
    
    // WA
    this.showListTik = false
    setTimeout(() => {
        this.showListTik = true
    }, 10)

    return elements
}

Wroks well for me :)

GrRivero avatar Nov 23 '22 15:11 GrRivero

I am seeing this behavior when running Array.sort on the items, and the list doesn't refresh unless user starts scrolling.

** Update **

Confirmed I can get this working with this workaround

watch(
  () => computedEntities.value,
  _ => {
    isUpdating.value = true

    setTimeout(() => {
      isUpdating.value = false
    }, 10)
  },
  { deep: true },
)
</script>

<template>
  <VirtualScroller
    v-if="!isUpdating.value"
  >
    ...
  </VirtualScroller>

qualle-admin avatar Jan 23 '23 17:01 qualle-admin

    v-if="!isUpdating.value"

A less-than-ideal solution, but it worked... For me was too much to refresh a complete list of items when a single one is changed.

My workaround was to fork the source code and add an additional property and event into the RecycleScroller to listen for a change and execute the method this.updateVisibleItems(false), the emitted event was used to revert the value to default (for a next change in the same item).

I don't know why the author of the project does not attach this method updateVisibleItems to some kind of property with options for additional external change. My socket update was not able to display up-to-date list information.

I could share my fix with more details if there's interest.

fire1 avatar Feb 04 '23 05:02 fire1

I actually fixed this. sorry for late reply - I do it by assigning a new index on sort like this:

So every list that is passed in, whether 999 - 0 or 0 - 999 always has an index of 0 - 999

const mutableEntities = ref(
  useArrayMap(entities.value, (entity, index) => ({
    ...entity,
    index,
  })).value,
  
  const computedEntities = computed({
  get() {
    return mutableEntities.value
  },
  set(value) {
    mutableEntities.value = value
  },
})

<VirtualScroller
    emit-update
    :items="entities"
    v-slot="{ item, active, index }"
/>

qualle-admin avatar Feb 04 '23 06:02 qualle-admin

@fire1 if you don't mind, could you please share your code?

eliottvincent avatar Feb 06 '23 16:02 eliottvincent

when there are thounds of data, and scrolling very fast with the scrollbar, this problem reappears easily. image

Demo: https://codesandbox.io/s/vue-virtual-selector-60sds?file=/src/App.vue

props updateInterval can make it work

Creazybird avatar Aug 10 '23 14:08 Creazybird

when there are thounds of data, and scrolling very fast with the scrollbar, this problem reappears easily. image Demo: https://codesandbox.io/s/vue-virtual-selector-60sds?file=/src/App.vue

props updateInterval can make it work

Could you elaborate? I couldn't make it work :/ Any help would be greatly appreciated

OfekA-IAI avatar Jan 30 '24 07:01 OfekA-IAI

@fire1 if you don't mind, could you please share your code?

Sorry, but I over-customized the source code for an application to optimize it to work with 10k rows per 5 cols with dynamic data.

fire1 avatar Feb 17 '24 10:02 fire1

I'm facing this issue as well, and hiding the list when updating the data is not an option for me, so does anyone have a better solution to fix this issue ?

adoniscoder avatar Apr 02 '24 08:04 adoniscoder

I'm facing this issue as well, and hiding the list when updating the data is not an option for me, so does anyone have a better solution to fix this issue ?

After looking for many solutions, we ended up adding :update-interval="100" as a prop. It's not 100% bullet proof, but we went from it happening like 10% of the time to 0.01%, so extremely rare.

OfekA-IAI avatar Apr 02 '24 15:04 OfekA-IAI