-
Notifications
You must be signed in to change notification settings - Fork 3k
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
[HOLD #147480][$40,000] React Native ScrollView bug: maintainVisibleContentPosition #7925
Comments
Triggered auto assignment to @kadiealexander ( |
@kadiealexander reassigning to @mallenexpensify because we discussed this 1:1 |
Triggered auto assignment to Contributor-plus team member for initial proposal review - @parasharrajat ( |
Current assignee @roryabraham is eligible for the Exported assigner, not assigning anyone new. |
I'm not sure I get the issue correctly since, when setting: <ScrollView
automaticallyAdjustContentInsets={false}
maintainVisibleContentPosition={{ minIndexForVisible: 0 }}
// …
RPReplay_Final1646045092.MP4<ScrollView
automaticallyAdjustContentInsets={false}
maintainVisibleContentPosition={{
minIndexForVisible: 0,
autoscrollToTopThreshold: 0,
}}
RPReplay_Final1646045125.MP4<ScrollView
automaticallyAdjustContentInsets={false}
maintainVisibleContentPosition={{
minIndexForVisible: 1,
autoscrollToTopThreshold: 1,
}} This case is really broken, with RPReplay_Final1646045208.MP4@roryabraham Maybe I'm missing something? |
@zoontek Thanks for following up. You may have missed this piece of the issue description:
|
@roryabraham No, I read it, but as I understand setting any |
Hi @zoontek – so you're saying that Nonetheless, I think this is still a valid bug report in that |
@mallenexpensify Let's double this. |
Price doubled to $20,000 |
@roryabraham could you take a look at this code please: https://github.com/azimgd/MVCPExample/
Here is the demo which maintains visible content position in both directions, is that the actual result you are looking for in this issue?: Simulator.Screen.Recording.-.iPhone.13.-.2022-03-09.at.21.34.44.mov CGRect newFrame = self->_firstVisibleView.frame;
- CGFloat deltaY = newFrame.origin.y - self->_prevFirstVisibleFrame.origin.y;
- if (ABS(deltaY) > 0.1) {
+ CGFloat deltaY = self->_scrollView.contentSize.height - self->_prevContentSizeHeight;
+
+ if (ABS(deltaY) > 0.1 && newFrame.origin.y != self->_prevFirstVisibleFrame.origin.y) { This will store the content size before and after new items are added. Delta will be calculated accordingly and correct content offset is applied without triggering Changing |
could you take a look at this code please: https://github.com/Sarveshwins/ScrollViewbug Here is the demo which maintains visible content position in both directions. Screen.Recording.2022-03-10.at.12.05.33.PM.movconst [sizeOfHeight, setsizeOfHeight] = useState(40);
}; const DeletAtBottom = () => { |
Looking at proposals shortly... Need to understand the problem first. |
Is there a link to comment linking discussions or a doc being worked on? |
Job added to Upwork: https://www.upwork.com/jobs/~01fe321bebf9b78f69 |
Triggered auto assignment to Contributor Plus for review of internal employee PR - @aimane-chnaif ( |
Not sure if C+ is explicitly required at this point, I mainly wanted to clear up that we have a plan that we are working on internally, thus I update the issue with the |
Since this is a big change, I can help review and test fixes if needed 🙂 |
@janicduplessis @roryabraham can you provide an update? |
Upstream PR is in review: facebook/react-native#35993 |
@janicduplessis can you reply to the comments in the PR - facebook/react-native#35993 (comment) Pasting here in case others have ideas
and
|
From @janicduplessis last week
|
All of the PRs have now landed upstream! |
It looks like facebook/react-native#35993 (comment) is closed, is there anything else we're waiting on here? |
It's just a documentation update, but I think the last PR we want to see merged before this is 100% complete is facebook/react-native-website#3721 |
Well I guess that was quick :) |
📣 @darkbasic! 📣
|
facebook/react-native-website#3721 merged. Closing this out! Great job @janicduplessis |
I'm assuming no contributors are due compensation via Upwork. Please comment if you disagree. |
If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!
Action Performed:
Here is a very minimal reproduction of this bug in the rn-tester sample project, that's not dependent upon any new code in the React Native codebase. Run the rn-tester app, find the
ScrollView
example, and observe the following behavior:If
minIndexForVisible
is0
, then the scroll position will be maintained iff:contentOffset
is at leasty
, wherey
is the height of the new content being prependedSimilarly, if
minIndexForVisible
is1
, then the scroll position will be maintained iff:contentOffset
is at leasty
, wherey
is the height of the new content being prependedAnd so on... If either of those conditions are not met, the scroll position will not be maintained.
Expected Result:
As long as the list has enough content that it is scrollable, the
contentOffset
should be adjusted such that the scroll position is maintained when new items are added to the start or end of the list.Actual Result:
The
contentOffset
is not adjusted, and the scroll position is not maintained.Additional Details
Background
How does the
maintainVisibleContentPosition
prop work?At a high level, when new UI elements are added to the start of the list, React Native will:
contentOffset
of the scroll container by the amount calculated in the previous step, such that:However, this prop does not work consistently – sometimes in step 3 the difference in position is incorrectly calculated to be zero. Furthermore, we have noticed that this seems only to happen consistently when the content length of newly-prepended list items is long.
Motivation
For a few months now, we have been endeavoring to get a working solution for a bidirectional-scrolling virtualized list in React Native. After working through many potential solutions, we have come very close to a working solution directly in React Native's VirtualizedList through the code in this PR. However, after lots of debugging we determined that the issues we were seeing weren't caused by the JS /
VirtualizedList
at all, but instead by this bug in ScrollView'smaintainVisibleContentPosition
prop, which is implemented in the native layer.The result of this bug is that our implementation of the
onStartReached
prop inVirtualizedList
suffers from the following issue:contentOffset
of0
),onStartReached
is called.onStartReached
prepends new items into the list.maintainVisibleContentPosition
fails to update thecontentOffset
to account for those new list items.contentOffset
is still0
, so the list position jumps to the start of the new content.contentOffset
is0
,onStartReached
is called again, and we get an infinite loop (at least, until there's no more content to load).Android considerations
Another important piece of information is that the
maintainVisibleContentPosition
prop is not yet available on Android (implementation in progress). We have examined the in-progress Android implementation and found that it is very similar to the iOS one, and likely shares the same problem.For the sake of this issue, the scope is focused on
iOS
, but we believe that the solution in one platform will be applicable in the other.Potential cause
According to review comments from a Meta engineer, this bug is likely caused by a race condition between the items being added to the list and content offset being adjusted.
They also suggest implementing a binary search for the first visible item, which seems like it might improve the issue, but (in my opinion) is unlikely to resolve the race condition entirely.
Evidence of potential race condition?
In the FlatList example linked above, if you tweak these parameters as follows:
The problem is mitigated but not completely solved (a few pages load before
maintainVisibleContentPosition
seems to "catch up" and function as expected). This hints that the problem may indeed be a race condition as suggested above.autoScrollToTopThreshold
wonkinessAccording to the React Native documentation:
This suggests that with an
autoScrollToTopThreshold
of0
, then no auto-scrolling should occur if you have a non-zerocontentOffset
before new items are appended to the list. We have observed that this is not the case by:minIndexForVisible
item out of view)Despite having an
autoScrollToTopThreshold
of0
and a non-zerocontentOffset
, theScrollView
auto-scrolls to the top.Interestingly, this particular
ScrollView
example listed in the reproduction steps can be fixed by removing theautoScrollToTopThreshold
parameter entirely. While this might be a hint at how to solve this, it does not seem to be a viable solution for us. Even without anautoScrollToTopThreshold
parameter, the same problem occurs in this FlatList example. It's unclear why removing theautoScrollToTopThreshold
parameter fixes the problem, but setting a value of0
does not. 🤔Workaround:
While there may be workarounds possible via hacks in the JS code involving
setTimeout
or extra calls toscrollToIndex
/scrollToOffset
, these would not solve the root problem. In order to have a proposal accepted, it must fix the problem in the React Native codebase itself, probably in the native layer.Platform:
Right now, this problem has been confirmed on iOS. It very likely exists on Android as well in this implementation. We are working on confirming the issue there, and will be following the progress of that pull request.
For the scope of this issue, we'll only require a fix in iOS or Android (preferably iOS), submitted as a PR against the our react-native fork: https://github.com/Expensify/react-native. Applying the same fix in the other platform should be comparatively easy and can be treated as a follow-up.
View all open jobs on GitHub
Upwork Automation - Do Not Edit
The text was updated successfully, but these errors were encountered: