Uh oh!
There was an error while loading. Please reload this page.
Add maintainVisibleContentPosition support on Android for legacy renderer - #35049
Add maintainVisibleContentPosition support on Android for legacy renderer#35049janicduplessis wants to merge 2 commits into
Conversation
Base commit: 8c2a4d0 |
Base commit: 8c2a4d0 |
47f93e1 to
c069ae3Comparepull-bot
commented
Oct 22, 2022
PR build artifact for c069ae3 is ready. |
pull-bot
commented
Oct 22, 2022
PR build artifact for c069ae3 is ready. |
facebook-github-bot
commented
Oct 24, 2022
@skinsshark has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
mmmoussa
commented
Oct 31, 2022
I have tested this PR out in my app (closed-source) which is using |
ryancat
left a comment
There was a problem hiding this comment.
Please take a look at the comment and make sure there's no race possible here.
| Rect newFrame = new Rect(); | ||
| firstVisibleView.getHitRect(newFrame); | ||
| if (mHorizontal) { |
There was a problem hiding this comment.
If you could get rid of the branching logic for horizontal vs vertical scroll view, that would be great!
Branching and interface are two different ways we handle variants. Adding branching will usually be cleaner, but will create duplicate code easily. On the other hand, using interface to abstract the logic will be more challenging in coding, but the result is more flexible.
You may use ReactScrollViewHelper as an example. There's no branching there so duplication is reduced.
There was a problem hiding this comment.
Do you have an example of the interface you have in mind?
My thinking when organizing the code like this was to reduce duplication between the 2 implementations while also moving all code related to maintain visible content position out of the scrollview classes to avoid making them too big. The common logic when the variant specific is also very intertwined in some cases, see https://github.com/facebook/react-native/pull/35049/files/c069ae3e39595e7d7e7a3259c6004d1313ad6ba8#diff-b0c8b7c192810637724d9d2ed35db2b8921c45e153ebbda1ef8f34cb53837473R153 for example.
| if (deltaX != 0) { | ||
| int scrollX = mScrollView.getScrollX(); | ||
| mScrollView.scrollTo(scrollX + deltaX, mScrollView.getScrollY()); | ||
| mPrevFirstVisibleFrame = newFrame; |
There was a problem hiding this comment.
If I understand correctly, the newFrame is the frame for the new firstVisibleView, and you've updated the scroll position back to the previous first visible frame. Why do you need to update the previous frame here?
I thought you want to keep the scroll position unchanged, which means you would preserve the first visible frame?
There was a problem hiding this comment.
This is to avoid adjusting the scroll position multiple times. I had cases where this method would be called multiple times in a row.
| private long mLastScrollDispatchTime = 0; | ||
| private int mScrollEventThrottle = 0; | ||
| private @Nullable View mContentView; | ||
| private @Nullable MaintainVisibleScrollPositionHelper mMaintainVisibleContentPositionHelper = null; |
There was a problem hiding this comment.
By default it will be null, so this is technically not needed (https://stackoverflow.com/questions/16699593/uninitialized-object-vs-object-initialized-to-null)
| return; | ||
| } | ||
| View firstVisibleView = mFirstVisibleView.get(); |
There was a problem hiding this comment.
updateScrollPosition is getting called on layout change. Is this always going to happen after computing mFirstVisibleView, which here it does in listener to willDispatchViewUpdates?
In theory I think it should, as we will dispatch the mounting instruction in JS thread or UI thread (when sync update) before the native platform to update the layout. However, since we are doing the computing on UI thread asynchronously, I am not sure if there will be a race condition where the first visible view is computed after we called this method.
cc @javache what do you think?
There was a problem hiding this comment.
If willDispatchViewUpdates is called from ui thread (which I’m not sure if it ever does) the dispatch to ui thread will be done synchronously by runOnUIThread. Ideally we could have a way to observe before / after views are updated on the ui thread but wanted to avoid adding more methods to UIManagerObserver for old arch. In my tests this always worked so I was fine with this solution.
There was a problem hiding this comment.
@janicduplessisfirstVisibleView may be null, i ran into.E/unknown:ReactNative: Exception in native call java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.getHitRect(android.graphics.Rect)' on a null object reference at com.facebook.react.views.scroll.MaintainVisibleScrollPositionHelper.updateScrollPositionInternal(MaintainVisibleScrollPositionHelper.java:107) at com.facebook.react.views.scroll.MaintainVisibleScrollPositionHelper.updateScrollPosition(MaintainVisibleScrollPositionHelper.java:97) at com.facebook.react.views.scroll.ReactScrollView.onLayoutChange(ReactScrollView.java:1128) at android.view.View.layout(View.java:20717) at android.view.ViewGroup.layout(ViewGroup.java:6198) at com.facebook.react.uimanager.NativeViewHierarchyManager.updateLayout(NativeViewHierarchyManager.java:254) at com.facebook.react.uimanager.NativeViewHierarchyManager.updateLayout(NativeViewHierarchyManager.java:222)
There was a problem hiding this comment.
I see, yes it is missing a null check, I'll open a PR to fix it.
aweffr
commented
Nov 11, 2022
any progress on this? |
pull-bot
commented
Nov 12, 2022
PR build artifact for 2764db0 is ready. |
pull-bot
commented
Nov 12, 2022
PR build artifact for 2764db0 is ready. |
facebook-github-bot
commented
Nov 15, 2022
@skinsshark has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
@skinsshark@ryancat Any updates on this? I think the only unaddressed point is #35049 (comment). |
areebbajwa
commented
Dec 12, 2022
Need an update on this please. |
paula-morales
commented
Dec 27, 2022
Any update? 🙏 |
codal-mpawar
commented
Jan 2, 2023
ANy update on this? |
skinsshark
commented
Jan 3, 2023
hey all! thank you for your patience and sorry for the delay, there are a couple things we want to test internally but haven't had the bandwidth just yet- i will get this prioritized and be back with updates! happy new year! |
siu666
commented
Jan 7, 2023
@pull-bot could yours rebuild the tarball in CircleCI ?, cause it had been removed after 30days , thx~~~ |
matt-alice
commented
Jan 12, 2023
Hi, many happy returns and thanks for the update. |
areebbajwa
commented
Jan 12, 2023
I would like to know that as well |
janicduplessis
commented
Jan 24, 2023
Thanks @skinsshark !! |
react/react-native#35049 added support for this on Android
* Update platform label for `maintainVisibleContentPosition` react/react-native#35049 added support for this on Android * Update scrollview.md
So this maintanVisibleContentPosition prop is now available on what version of RN? And is available on FlatList as well, right? |
janicduplessis
commented
Apr 22, 2023
It is available in 0.72. It also works with FlatList but there are a few known issues that will be fixed in #35993 which isn’t merged yet. |
hantrungkien
commented
Jun 16, 2023
@janicduplessis I have tried it in 0.72-rc06. Everything seems fine but I found a problem with my chat app with scenario:
If all messages are short content then maintainVisibleContentPosition is working fine. But messages from 35-31 is very long text (greater than 3000 char) then FlatList still auto scroll to first position. Do you have any suggestions for me? |
…38891) Summary: `mFirstVisibleView` is a weak ref so it can also be null when dereferencing. This was reported on the original PR here #35049 (comment) ## Changelog: [ANDROID] [FIXED] - Fix null crash when using maintainVisibleContentPosition on Android Pull Request resolved: #38891 Test Plan: Not sure exactly in what cases this can happen, but the fix is trivial and makes sense. Reviewed By: cortinico Differential Revision: D48192154 Pulled By: rshest fbshipit-source-id: 57a38a22a0e216a33603438355bde0013c014fbf
…38891) Summary: `mFirstVisibleView` is a weak ref so it can also be null when dereferencing. This was reported on the original PR here #35049 (comment) ## Changelog: [ANDROID] [FIXED] - Fix null crash when using maintainVisibleContentPosition on Android Pull Request resolved: #38891 Test Plan: Not sure exactly in what cases this can happen, but the fix is trivial and makes sense. Reviewed By: cortinico Differential Revision: D48192154 Pulled By: rshest fbshipit-source-id: 57a38a22a0e216a33603438355bde0013c014fbf
haileyok
commented
Feb 2, 2024
@hantrungkien did you ever experiment further with that? thanks! |
janicduplessis
commented
Feb 3, 2024
We also have noticed some issues on android, I will look at it soon. |
@janicduplessis Following up in case this is helpful, I can open a new issue too: https://snack.expo.dev/@haileyok/flatlist-repro |
janicduplessis
commented
Feb 6, 2024
Thanks, this will help, planning to look at it this week. |
janicduplessis
commented
Feb 6, 2024
No need for an issue. |
perunt
commented
Feb 6, 2024
@haileyok, have you tried adding half the number of initial items? Also, as you mentioned, the |
haileyok
commented
Feb 6, 2024
my experience was that the number of initial items didn't affect anything. IIRC, even with just one initial item (and some padding on the I suppose my immediate question would be is this something that's a bug/could be fixed or is this a limit of the virtualization (I don't know all the specifics on how the virtualization works, just a quick review of the code here)? I'm implementing a workaround right now to artificially "load in" items when we scroll up, which is a fine solution (and maybe better anyway for performance so we don't immediately load a bunch of items) but would still be interested in knowing if this could be fixed. Appreciate it! |
janicduplessis
commented
Feb 6, 2024
For more context: Usually the problem with maintainVisibleContentPosition and virtualization is that We added some mitigations to try and prevent this from happening, but for some reason it seems to still happens some times on Android. |
janicduplessis
commented
Feb 6, 2024
@haileyok I just realized the issue you mentionned happens on iOS. It actually looks like an issue I addressed recently. Sadly I don't think the changes haven't made it into a release yet. Can you try to apply the changes from this 3ed4bf9. This code should be in the @react-native/virtualized-lists package. |
janicduplessis
commented
Feb 6, 2024
The issue we are currently experiencing seems to happen only on Android if adding items at the start of the list during scrolling. |
haileyok
commented
Feb 6, 2024
Ah, awesome! I'll look later if I have time today or tomorrow if not and let you know. Appreciate the quick response. I take it this addresses both platforms since it's just the JS side? |
janicduplessis
commented
Feb 6, 2024
@haileyok It does yes |
janicduplessis
commented
Feb 6, 2024
There is also one more case of sliding that I know of. It happens when items size vary a lot. Internally VirtualizedList uses the average size of previously measured items to estimate the size of new items being added before they get measured and the actual size can be used. In your repro if I increase number of item added to 60 it starts happening. |
miafoo
commented
Feb 27, 2024
I'm running into this as well unfortunately. Do you have an issue or PR tracking this, or will you keep us posted here? Just want to stay updated for when it's fixed 💜 |
haileyok
commented
Feb 27, 2024
@miafoo Can't give you any updates on that status, but what I can pass over is a bit of how we worked around the problem. One way that you might be able to get around this though is to only add items to the top of the list in increments. If you use |
janicduplessis
commented
Mar 12, 2024
wqhui
commented
Oct 22, 2024
awesome! Can 70.0.x still be supported? The react-native upgrade version is too difficult... |
Summary
This adds support for
maintainVisibleContentPositionon Android. The implementation is heavily inspired from iOS, it works by finding the first visible view and its frame before views are update, then adjusting the scroll position once the views are updated.Most of the logic is abstracted away in MaintainVisibleScrollPositionHelper to be used in both vertical and horizontal scrollview implementations.
Note that this only works for the old architecture, I have a follow up ready to add fabric support.
Changelog
[Android] [Added] - Add maintainVisibleContentPosition support on Android
Test Plan
Test in RN tester example on Android
Screen.Recording.2022-10-22.at.00.32.27.mov