-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynamic height + scrolling up (when the upper cells aren't loaded yet) #610
Comments
Unfortunately, I don't know a way to avoid this. In this case, we're essentially bypassing the measurements for all of the cells in the I'd say this is a bug, but I don't currently know of a way to address it. |
I was about to open an issue, when I noticed this. Something similar happens to me, but it is not only a visual issue, not just jumping around a bit... @bvaughn Can You tell from above is this related, or a separate issue? I can try to provide an example, if needed. |
Seems related enough to stay as part of this issue, @PTihomir. If you have any bandwidth I'd welcome a hand looking into this one. I'm a bit swamped at the moment. |
@bvaughn I tried some approaches, but as you said, it is not easy... My main idea was to somehow update the scrollTop of the scroll container when some of the rows are rendered (I was only considering List for the start) by the difference of the old and the new offset of the last rendered item. Yeah, it didn't worked, not sure why. Assuming that when I set the scrollTop manually, it disturbed the native scrolling. I can't really work more on this for some time, hopefully somebody will think of a solution. My half-workaround for now is to make better estimations. I created a custom cache, which will take for defaultHeight a function instead of a number. My list items have types, and all the items with the same type have almost the same height. This way my estimation is much closer to the real height. |
cant upgrade virtualized because of @PTihomir issue. |
@bvaughn |
Nope. I would welcome contributions. 😄 |
@bvaughn any direction for me to start ? |
Not really. It's a complicated issue to work on. Familiarize yourself with how |
@mushkab |
If there's interest from you both on collaborating on this- I'm supportive! I'd be happy to video chat to discuss questions or ideas about approaches, etc. I just don't have the bandwidth personally right now to work on it. |
@arusakov didnt get to it at all sorry :( |
@bvaughn is it possible to work around this issue by adding virtual InfiniteScroll, which would measure cells before "loading" them? |
I'm afraid I don't understand what you mean.
|
@bvaughn never mind, I don't think I had a right idea. However I stumbled upon chrome scroll anchoring but couldn't make it work with RV. Am I missing something or scroll anchoring algorithm is in principle not compatible with RV rendering? |
I don't think scroll anchoring should impact react-virtualized, except for maybe for the specific combination of |
Encountering the same issue as @PTihomir, just wanted to provide a link to an example demonstrating the issue with attempting to scroll up when using CellMeasurer / scrollToIndex |
Nice repro, @babsonmatt |
I am also developing a chat app @mushkab @arusakov, any later progress you guys made? this issue is really a headache, I performed a weird trick to solve it but not perfect, my solution is to remove the scrollToIndex method and call List.scrollToRow(lastIndex) twice, which will jump to last and avoid jumping, but I am still expecting @bvaughn would bring us a greater react-virtualized, as I am more devoted to android and ios developing work, not so familiar with H5, or I want to contribute my code ^_^ |
@mushkab @arusakov I have finished my chat app using react-virtualized, and my situation maybe more complex, my variable content is not just pure words, it can be emoji or stickers inside words, I |
Do you happen to be using that with InfiniteLoader @gordon-rawe ? I'm in a similar situation where the content is loaded as you scroll up and the height varies a lot. This combination is a real headache! |
I belive I cracked a solution. @wasd171 reference to Scroll Anchoring is totally right, and in fact I strongly believe that RV should implement scroll anchoring. Let me show you why. Normal scrolling works by keeping a In other words we can then say that the y position of the content is simply I’ll now try to demonstrate that this simple formula works only when the content is static, and that it’s just the special case where moving the “scroll anchor” would have no effect on the overall scroll. First of all what’s an “anchor”? You can consider it as the point in the content your are scrolling from. If you want a simple metaphor you can think of touch scrolling. When the user starts scrolling she is “touching” a vertical point in the content and moving it. This gives us Where
When the content never changes so do the y position of the anchor in the content, so Dynamic Anchor PositionSince dynamic load of rows is introduced in the system the position of the anchor becomes dynamic too, and this forces us to use the more complex formula, but if we now the position of the anchor (more on this later) before, during and after scrolling we can have smooth scroll interactions also with dynamic content. There’s a small but critical caveat here: the scroll position is not a managed state of the UI, it is independently managed by the browser and native scrolling functionality, and we have to keep it as such. This caveat means that we cannot treat the current content shift (read: scroll position) as a value we can easily replace by doing our calculations. But this is where React Virtualized shines: we can virtualize this behaviour by applying different approaches based on the situation of the interaction and (I believe) never collide with native scroll features. «At rest» approachMoving the scroll position when the user is not scrolling at all has little to no performances issues. When new heights are calculated for rows above the anchor we can simply update the scroll position too, keeping the anchor position in place. «Scrolling» approachThis will be trickier but it’s something only React Virtualized can do. When the user is scrolling we can shift the content above the anchor position by a correction factor that adjusts for the difference between the actual scroll position and the calculated scroll position. We can do this because we are not relying on the native vertical position algorithm for cells, placing them manually using absolutely positioned elements. To not interfere with native scrolling behaviour we also need not to change the height of the content. This therefore will move a part of the content outside the reachable area during normal scroll. Once the scrolling interaction has finished we can clean up by using the «At rest» approach. CaveatsIt can be tricky to correctly treat values passed to The same applies to scroll-to-top gestures that happen concurrently to row loading/height changes. Anchor PositionAnother challenge is how to calculate the position of the anchor. I propose a mock API here. A new
To the custom function the following values are passed: The For gridsMore or less the same approach applies to grids too, but the API need to be specified for both axes as an array of values For Collections and TablesI currently have no idea how this applies to those components. |
@yuchi Thanks for the explanation + context. It is extremely helpful and thorough. I am using List with dynamic heights and have hit the same issue. Is there anything I can do to help with the Anchor Position API you have proposed?? Happy to try and help implement for List if you could use the help. |
@bvaughn any update about this issue? |
Nope. It's tagged as "help wanted" 😄 |
Maybe this implementation can help? https://clauderic.github.io/react-tiny-virtual-list/#/controlled-props/scroll-to-index?_k=gwsgfc |
That looks to be based on this library 😅 at least comparing clauderic/react-tiny-virtual-list |
@bvaughn It totally is ❤️ |
Hey, So we're using react-virtualized for our chat component and we're running into this issue also. Basically we're always starting at 'the bottom' of the conversation and we want to be able to scroll up and load older messages. These messages are of varying heights. It seems like most of the machinery of List isn't built around this and assumes you want to always start at the top and append and grow downwards. I've started to experiment with using css to invert the list and I think its actually working better. I'm still ironing out some issues but I wondered if you had any thoughts about this approach. What I'm doing is basically using Not far enough for a fork/PR yet but wanted to add to the conversation |
That's an old trick in the native mobile development toolbox that has been used for years. The problem is that it could mess with scroll bars and such. I'm seriously starting to think scroll anchoring would be the solution to a whole class of problems. |
@chrisnojima Sorry for responding so late. I've been crushed by open source stuff lately and I can't keep up. I'm very interested in seeing your proof of concept. I've also got a couple of forks of RV that I've been trying to address the same problems with in a more generic way but so far, they aren't solid enough to consider releasing. |
Hey, |
Hey, ran into similar issue at work, managed to resolve it, but as for the moment i am actually using react-tiny-virtual-list with react-scrollbars. The idea is that i am fully controlling scroll there, but i managed to create similar solution using your library. At the moment there are quite few issues and i haven't done any performance tests using your library. Idea behind all this, that i took full control over scroll and caching size of rows. But the magic hides in items, as after they are rendered and updated, i read their height within component itself and pass it up for caching, where afterwards we can differentiate it and if scroll direction was going up, we can adjust scroll from top. componentDidMount() {
this.mounted = true;
// simulate ajax for each item
setTimeout(() => {
if (!this.mounted) {
return;
}
this.setState(
{
loaded: true
},
() => {
//get new height and update it in parent
let { height } = this.node.getBoundingClientRect();
this.props.onLoad(height);
}
);
}, 500);
} And this is called in parent after list item is rendered with updated height handleMount(index, height, cb) {
if (!this.customCache[index]) {
this.customCache[index] = height;
// default is 52
// get diff
let diff = height - 51;
if (diff && this.scrollDirection === "SCROLL_UP") {
let newScrollTop = this.scrollTop + diff;
this.scrollTop = newScrollTop;
this.setState(
{
scrollTop: newScrollTop
},
() => {
this.List.recomputeRowHeights(index);
}
);
} else {
this.List.recomputeRowHeights(index);
}
cb();
}
} Full code you can follow from here: Not a fan of this though - Pretty sure could combine this idea with CellMeasure or implement this in the library, catching scroll direction "up" (as scrolling down is working just fine), and adjust the scroll position, if row size is different than previous cache. A demo can be seen here - http://jurg.is/virtual-test/ Note for the demo: it is a lit buggy and code is not minified there so it's easier to understand. It will scroll almost to the bottom and you can start scrolling up slowly, to see the difference. Hopefully this will help someone :) |
Thanks @jurgenzz |
@chrisnojima genius on the flipping technique! |
@chrisnojima @andrewvmail this doesn't work. You just get other problem. See facebook/react-native#14520 (comment) :
|
just a heads up, i've abandoned using react-virtualized for this and made my own solution short term. I do want to use the next version of this (react-window) where it'll hopefully work great. You can watch the issue in that repo: bvaughn/react-window#6 |
@Bessonov , @chrisnojima havent tried adding items yet but what kind of
issue do you guys have? my use case is just need to add to bottom not top.
i guess ill find out
will watch react window thanks for the heads up!
|
In our case, the list items height isn't just dynamic, but the actual list is also dynamic, adding items to it by the user scrolling upwards (think chat). Our issue, however, is solving itself if we delay the focus on the row until we are sure that the recompute has happened. With this in mind, wouldn't it have made sense to offer a callback function out of when recomputeRowHeights() has run, and then allow the user to scroll to the index (or perform other actions if needed)? |
- huge messages bvaughn/react-virtualized#610 - doesn't start at bottom - image height - context menu missing - scrolling is lagging (throws you forward and backwards a bit while scrolling) - and likely even more bugs
- huge messages bvaughn/react-virtualized#610 - doesn't start at bottom - image height - context menu missing - scrolling is lagging (throws you forward and backwards a bit while scrolling) - and likely even more bugs
- huge messages bvaughn/react-virtualized#610 - doesn't start at bottom - image height - context menu missing - scrolling is lagging (throws you forward and backwards a bit while scrolling) - and likely even more bugs
I'm facing the same issue on react virtualized 9.21.1 |
How to reproduce this issue: go to https://bvaughn.github.io/react-virtualized/#/components/CellMeasurer , select dynamic height text, click on the table, press the End key and start scrolling up. You should see that the cells start "jumping" when loading the content.
I'm implementing similar set of components on my own, and I got the same problem. So I wonder if it's technically possible to render it smoothly. I suppose we could adjust the scrolling position and make it look smooth, but I can't wrap my head around it.
The text was updated successfully, but these errors were encountered: