petyosi / react-virtuoso

The most powerful virtual list component for React
https://virtuoso.dev
MIT License
5.25k stars 301 forks source link

Keep the scroll position - `scrollTo` #168

Closed shoNagai closed 3 years ago

shoNagai commented 4 years ago

First of all, thank you for this great work. I'm using this library to create a chat app like Slack, but there is one feature I'd like to achieve. That is to restore the previous scrolling position when view the chat list again. (e.g., revisit it after changing channels.) To implement this, I want to get and keep the scroll position.

I think initialTopMostItemIndex is the best way to specify the scroll position at the display, but the value for this is Index. Do you have an idea to achieve getting the Index at the scroll position?

At the moment I haven't come up with this idea and am trying to achieve it by customizing the scrollContainer and saving the scrollTop. In this case, I'm trying to make an offset version of initialTopMostItemIndex.

petyosi commented 4 years ago

@shoNagai this is not conveniently exposed due to a problem that I am yet to figure out how to resolve. It spans beyond the component itself - it is more of an issue for the app and the data provider.

Imagine the list containing 50,000 items, which are fetched from a remote data source. By default, the component "probes" the default item height by rendering the first item in the list - this means that the corresponding data (data window from item 0 to item 20 for example) fetch should occur.

For chat apps, the scroller is usually reversed - you are likely showing the latest messages. Fetching the first message is useless (and probably slow, btw) - you don't want to read those. That's why the initialTopMostItem property causes the component to probe the default item height with the initial topmost item index, saving the rendering (and the requirement of fetching) of item 0-20.

I can easily introduce a scrollTo method or initialScrollTop property. However, unlike the initialTopMostItemIndex, the component has no idea what's the data frame at the specified location - so it will have to probe with Item 0, causing the effect which I am trying to avoid. That's something which I can't figure out how to address. Requiring a defaultItemHeight can be a workaround, but I don't feel well for such a convoluted solution.

Now, let me try and be helpful for a change. Based on what you describe, it sounds like you are reusing one component instance for multiple channels. If this is so, I would recommend against doing this. Since each channel has different sized entries, this can cause lots of internal data structure trashing and scroll readjustments if the user scrolls up in one channel, switches to other, and scrolls up there. Using multiple Virtuoso instances can actually remove the need for you to save and restore the scroll position.

Hope this helps,

shoNagai commented 4 years ago

@petyosi Thank you for your kindness. You're right, even with ScrollTo I'm finding it difficult to scroll to the correct position.

Using multiple Virtuoso instances can actually remove the need for you to save and restore the scroll position.

This idea hadn't occurred to me. I'll refer to it.

Thanks for the great work.

PaperPlane01 commented 4 years ago

+1 for this, it would be useful :+1: I was able to achieve acceptable results by using rangeChanged callback and keeping track of startIndex for selected chat, and then using scrollToIndex method when going back to this chat. This isn't perfect and I had to apply some dirty hacks but it kind of works :) Unfortunately, this method doesn't work well if you are using overscan property because in that case rangeChanged callback reports indexes of rendered items, and the item with startIndex will probably not be actually visible, thus resulting you ending up with wrong scroll position. And in my case, rendering items without overscan on a smartphone leads to notable delays between scrolling and rendering items. To overcome this, I'm using react-visibility-sensor to keep track of last visible item, and I'm getting acceptable results. I'm sharing my code to anyone interested, maybe this will help someone https://gist.github.com/PaperPlane01/d8bc7fb9dcff06cc86fba220d0d9417d

nilo-makedev commented 4 years ago

@shoNagai I'm having the same issues, I'm wondering if you have solved this issue?

@petyosi Thanks for this awesome package. Looking forward fo the new updates.

petyosi commented 3 years ago

@shoNagai I caved in and introduced an initialScrollTop property in v1.1.0. Use responsibly and read the long explanation above.