-
Notifications
You must be signed in to change notification settings - Fork 24.6k
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
Improve inverted VirtualizedList implementation #23632
Conversation
Use flex instead of complex transform inversion. Better for performance, readability and provides natural behaviour for onEndReached. Signed-off-by: François Billioud <[email protected]>
This comment has been minimized.
This comment has been minimized.
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks! |
Signed-off-by: François Billioud <[email protected]>
@Sharcoux for the "Test Plan", can you fill out how exactly you verified that your PR fixes the described issue and how are you making sure it does not introduce regressions? |
How does this actually invert the scroll direction? It should start at the bottom, like a chat, rather than at the top, like a feed. Can you describe more specifically what’s wrong with onEndReached? A screencast if walking through all the options in the FlatListExample would be a good part of a test plan. |
The problem encountered is described here. You can experiment it with this sample. @sahrens The idea here is to use the @cpojer Actually, I had trouble finding out how to test my changes. What I ended up doing was to replicate my changes directly into the nodes_modules of the sample project provided above. What is the easiest way to import my changes into the project ? I don't understand the build process... Or should I create an account on npm and publish my own package? Am I the only one thinking that reversing the coordinate system of the ScrollView container and reversing again the system for the children Views is kinda hacky and doomed to introduce weird behaviours? |
Sorry for closing, I misclicked... |
Hi @Sharcoux, you can use the example for my issues about bugs with inverted lists for testing and fixing it. Let's work together to fix all of these. https://github.com/terrysahaidak/ReactNative-SeactionList-Bug-Example So far I've tested your changes with latest RN and my example here is the result: |
Oh perfect @terrysahaidak, thanks. I told you, I manually reported the changes I made from the built react-native files in the node_modules, so I probably messed up some part. Still, I don't understand how I am supposed to import my changes within your project. I see that you used |
@terrysahaidak I just inverted |
@Sharcoux that's the latest one published on NPM. As for me, the easiest way will be making changes directly in the node_modules of that repo. That allows you to see them as soon as possible. The other way is cloning RN's master somewhere, building from sources and linking the built version via |
Signed-off-by: François Billioud <[email protected]>
@terrysahaidak so there is no build process, ok. |
Signed-off-by: François Billioud <[email protected]>
65454fb
to
d8c257e
Compare
Ok, I fixed the insertion of separators. The current code for VirtualizedList is quite unsatisfying in my opinion. But I'm afraid it might be too late for a good clean up. I just kept things in phase with the current spirit to not break anything, but I'm wondering if it won't reveal performance issues for large lists. |
I'm sorry, I'm not sure I get the difference. From your screencast, the only difference I see is that the scroll initial position is at the bottom, which I could probably fix if it's what is intended. Could you confirm? |
I'm not sure the issue is in Yoga - it's more about the behavior of the native ScrollView component. Just setting the initial scroll position is also not sufficient - you also need to change the behavior when appending new content - if you're looking at the first items (first elements of the children array) and then you add more, without doing the transform and relying only on that initial scroll position, the new items will push the first item down off the bottom of the viewport, one of the main things reverse mode is supposed to avoid. |
More fundamentally, I don't understand your concerns and motivations in the first place. What is the problem with performance? What are you trying to achieve? What's the problem with |
And yes I agree the code is not in a great state - it has evolved a ton with a bunch of complex features getting tacked on over the years that have complex interactions with each other. You don't have to use it though - you can use ScrollView directly, or you can use https://github.com/Flipkart/recyclerlistview, or you can fork VirtualizedList and strip out the features you don't need to simplify it - it depends on what you need to accomplish. |
And I agree that ScrollView with row-reverse should follow the w3c spec, but that's a whole project that is pretty low-pri at the moment. I think it can be done outside of Yoga though, but it will probably be pretty tricky. |
Thank you very much for your commitment to the topic.
I think that is already covered by
I am not sure about others but I am facing some problems on AndroidTV where such transformation is causing the scroll to work in opposite direction (AFAIK it is not yet reported anywhere). It happens when the user navigates using remote control and directional navigation takes place - the element in opposite direction to the desired one gets focused and the scroll direction is therefore reversed. Correct me if I am wrong, but the current implementation is a bit hacky. It works by transforming whole view along one axis and transform every children ( IMO the implementation with |
Using a transform should actually be very performant because it's a simply matrix operation performed by the GPU. We haven't seen any noticeable performance impact in our usage. Can you elaborate more on the issues you're seeing on AndroidTV? Why are you using inverted in the first place? The whole point is to reverse the scroll direction. |
I see, so it looks like the performance should not be our concern even on some low-end GPUs and when there are a lot of elements?
How about introducing some new props on
Then it could be simply used in |
I think I was wrong on this one - this would only help for the initial scroll position, but the order of elements would be incorrect as they would get mirrored-back. |
Hmmm... So on mobile devices the user needs to swipe in reverse direction than usual? I thought it was meant to reverse the item order and initial scroll position so that e.g. the element list grow upward. I have something like a bottom navigation bar from which I am choosing the screen. If the screen contains list of tiles it is natural to start from the first one after pressing UP and scroll upward to further ones. The problem is that the directional navigation using DPAD on remote is working in opposite direction. Please see the description that I have added in my previous comment: #23632 (comment) (starting from It happens) Edit: |
Oh I see, yeah the DPAD and touch gestures should all move the scroll in the same natural direction. Sounds like a bug in the AndroidTV ScrollView implementation or whatever primitive it's based on, especially if it works correctly on iOS, Android, and AppleTV. Who maintains AndroidTV? I'd focus on them. |
Maybe ask @krzysztofciombor - he added DPAD stuff in b7bb2e5. Or @aamalric for 00c8b3c Maybe muck with stuff in here? react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java Lines 301 to 309 in 3b6f6ca
|
Thanks! 👍 I will look on it a bit deeper but I still can't help the feeling that it could be fixed by improved version of this PR. :-) |
If you check here, you can notice the bug that @dblachut-adb was talking about, but there is worse. The onEndReached event is introducing weird behaviours when you scroll up and load more than 100 items. This is caused by the current hack in the Flat-List. The only explaination I have to explain why the current code works on Android and iOS is that there should be a "counter-hack" in the native views that will fix the hack we are introducing here. IMO, this is really not sane. |
@sahrens @dblachut-adb I really don't understand guys... Instead of fixing |
@Sharcoux I don't have a very good understanding on the responsibility of each react-native component, but if I am not mistaken yoga is the library which calculates the measurements for views and defines how the views are laid out on the screen. It does not contain any information about the initial scroll position nor it is not able to set it. That was my concern and I asked react-native team if the potential fix should really be there. |
BTW with your changes the initial scroll position would have to be changed on each platform. It will also have problems with the scroll position as reported by @terrysahaidak which means that in inverted mode:
Unless I am wrong and this can really be fixed in yoga, which is common for all platforms, and hopefully the scroll position would then be reported/processed correctly. |
But you need to separate the concerns. The objective of VirtualizedList is to provide a React-Native implementation of a list based on CSS/flex styling. "some events may report wrong scroll position" -> That's exactly the opposite don't you think? If you draw all components on a reversed axis but with reversed-reversed transformation, don't you think that it is very likely that the detected scroll position will be wrong, or at least hard to make sense of? What I mean is that VirtualizedList should solely rely on the implementation of I'm not sure I'm being clear, so sorry if I'm confusing. |
What And with the current implementation, if I want to scroll to the start of an item in an inverted FlatList, what will it be? Because according to the VirtualizedList, the start of the item is actually the end of this item being drawn backwards... So there is 100% chance of what I'm trying to accomplish giving me headhache after 10 minutes. |
Yes, maybe the issue should be addressed in Yoga, but my concern was that the Yoga possibly does not maintain the position of scroll. In such situation it would need to send some information outside it or it should be implemented elsewhere.
I was under impression that the goal is to change the underlying implementation without breaking existing API and applications. If the goal is to follow the spec of It may have more sense to have common position calculation on both modes. I guess it basically depends what do we want from "inverted" mode. If it should simply reverse the item order and change initial scroll position (the same as Currently the "inverted" mode is something more than the web
If those methods are implemented in the View ( Don't get me wrong - I understand your concerns about the position calculation and the "badness" of current implementation. I also like your approach to implementing the "inverted" mode, but currently, it is a bit different in usage than previous solution. :) |
@@ -921,19 +912,14 @@ class VirtualizedList extends React.PureComponent<Props, State> { | |||
onScrollBeginDrag: this._onScrollBeginDrag, | |||
onScrollEndDrag: this._onScrollEndDrag, | |||
onMomentumScrollEnd: this._onMomentumScrollEnd, | |||
contentContainerStyle: inversionStyle, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this.props.contentContainerStyle
is lost, please use array of styles or maybe StyleSheet.compose
(I am not sure what is the difference).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I probably got wrong what inversionStyle
was supposed to be for. Ok, I should fix that indeed if it actually is the contentContainerStyle.
The position of the scroll is probably managed in the ScrollView, so everything related should stay there don't you think?
I think that there might be some minor braking changes if people already hacked their way through
I think that as long as it is consistent it should be ok. We can reverse the axis or keep the same axis both ways. My concern is just that with current implementation, the offset inside one child goes on the opposite direction as the offset of the scroll view. Usually,
As long as you don't pretend that
What I mean is that it is unrelated because deciding this doesn't mean we should change the axis direction. I mean that if we decide that the End is the top in inverted lists, we just need to make it behave this way in the implementation of these functions. Reversing the axis will indeed have an effect on the implementation but should be transparent as for the result. I'm not sure if I'm clear...
I think there could be a difference in the result of the |
Yes, that was exactly my point.
With such proposal, to keep the same functionality, we would need something like
Yes I understand you. For me the best solution would be if the functionality would be the same and the position would be consistent (independent from using inverted mode or not). The alternative is to keep the same functionality and maintain the reversed position in reversed mode.
My point was that it may have more value to provide some functionality than follow the standards of different specification in this case. If we can have both - even better.
Yes, I totally agree.
What I meant was that if this commit breaks something - the doc should be updated. And it should not remove some functionality that previously was present. |
Thanks for your reply.
I totally disagree on this. The End should always refer to the direction where the last item in the list is represented. Thus, the end should be the top on reversed lists, and the bottom on normal lists. If you create |
I have just said that I wasn't sure if Yoga is able to do this. That was just the thing I was afraid of, nothing more (I don't have the knowledge in this area). ;-)
Yes, that would be great.
Right now e.g.
You have understood me wrong, the rename would be needed in your current implementation, in which
Ah, my bad, I have forgot about the horizontal mode. |
I agree on Oh! My bad for scrollToEnd. I didn't see it being implemented here. Indeed, it should be fixed too. |
If anyone is looking for an incentive to fix this aspect |
We're still looking for help to "Fix Inverted prop on the FlatList component with a RN Patch". The current link is Expensify/App#3381 job amount is $8000. Any help and recommendations are greatly appreciated. |
Use flex instead of complex transform inversion. Better for performance, readability and provides natural behaviour for onEndReached.
Summary
The current implementation of
inverted
inVirtualizedList
introduces a lot of problems, especially combined with theonEndReached
events. You can see it here.The scroll is reverted in an unnatural way, the onEndReached is not emitted at the right moment, and the scroll bar seems to be teleported randomly.
Changelog
[General] [Fixed] - Better implementation for
inverted
andonEndReached
onVirtualizedList
Test Plan
Would you mind helping me on this part? What should I do exactly?