In larger complex view hierarchies it may happen that a ScrollView
-based component is nested inside another ScrollView
.
Currently a nested ScrollView
wonât receive scroll events, only the outer parent ScrollView
will. In order to receive them, you would have to push scroll event callbacks down _each_ child component of the parent ScrollView
and all of those would have to send them down to their children, just in case a component somewhere down the tree needs to receive such events.
The developer and user of that component shouldnât need to jump through hoops to have ScrollView
callbacks work, but should be able to write isolated components that are unaware of the presence of other ScrollView
instances in the view hierarchy and have React Native deal with the details of dispatching events to all components that require them.
Multiple ListView
instances as part of a larger scrolling view:
<ScrollView>
<Header />
<ListView />
<ListView />
<Footer />
</ScrollView>
Often the advice is to add views that are supposed to be above or below a ListView
as a header or footer of the ListView
, but that doesnât solve the case where both ListView
instance have to scroll in tandem as part of the parent ScrollView
.
My real world example is a âtab viewâ component that displays a ScrollView
-based component that needs to paginate.
You can run the example app thatâs contained in the repo to see it for yourself, or in our production app that is currently in the store, but hereâs a short demonstration of the âWORKSâ grid component being nested inside the top-level ScrollView
and still paginating onEndReached
as expected:
For my app I wrote some đ-patches that do the following:
RCTScrollView
is added to a view hierarchy, it registers for a RCTScrollEvent
notification.RCTScrollView
receives a native scroll callback it first does its own JS scroll event dispatching and afterwards it posts a RCTScrollEvent
notification for any child RCTScrollView
instances to pick-up.RCTScrollView
instance creates a new RCTScrollEvent
object that offsets the scrollviewâs contentOffset
to reflect its location inside the parent RCTScrollView
.RCTScrollView
then dispatches the JS scroll event as normal and the component doesnât know any better than that it has scrolled as normal.Notes:
NSNotificationCenter
because thereâs a potential 1-to-many relationship. I havenât noticed any discernible lag in delivery, but thereâs also no guarantee. A possible alternative would be to use a proxy object as the UIScrollView
delegate that can dispatch events to multiple delegates (example).ListView
component in the current version of our app, I have tested and verified that the onEndReached
callback gets triggered, thus I see no reason why the removeClippedSubviews
optimisation wouldnât work either. At least thatâs my goal, as I will need this soon.ScrollView
into a Scrollable
container. That container could then create the ScrollView
if not inside a parent ScrollView
./cc @javache @nicklockwood
đ Beautiful issue.
Good job
@facebook-github-bot label Good First Task
I don't think that's a good first task @charpeni ?
I agree, this will require some more significant changes in how we create scrollviews and dispatch scroll events.
I'm running into this same issue. In iOS it seems to work as expected for me, but in Android the inner ScrollView never receives horizontal pan gestures if it is contained in a ScrollView that can also scroll horizontally, e.g. a zoomed image in a gallery
@alloy just ran into this, any progress on the issue?
The provided monkey-patch still works perfect in production with latest RN. I havenât had much spare time to discuss and cleanup this patch until last week when we shipped a new major version of our app.
@javache Can we catch-up sometime soon to talk it through?
Guys, adding TouchableWithoutOpacity inside nested ScrollView worked like a charm for me on android:
Checkout my answer on SO (couldn't make the markup right in here..)
Maybe it's not the case though...
With 0.43.0-rc.2
I'm running into the same problem when attempting to wrap a ScrollView
around the new FlatList
component; the onEndReached
event won't triggered as expected
@ilyadoroshin TouchableWithoutFeedback component worked for me on iOS.
@AlanFoster Same with you. onEndReached
won't called if a
Has anyone other than @alloy found a workaround? Even if it's a monkeypatch approach, I am trying to resolve this.
@ilyadoroshin @restlessCalm FYI The issue described in the linked SO post is about a completely different scenario than what this ticket is about.
@AlanFoster @andrerfneves Did you try my monkey patches in latest RN and/or with FlatList? I havenât upgrade to the latest yet, but the internal scrollview API hasn'tât changed much over the months, so should probably be ok.
@alloy how would you go about using such monekypatches? Not entirely familiar with iOS development. My issue is actually the same but on the Android platform. Any thoughts?
Ahh I see. No sorry I have no input on Android right now, only iOS. As to how to integrate these monkey patches, you can take a look at our OSS project they reside in.
Any solution to nested scroll views on Android?
I have a vertical SectionList
inside a vertical ScrollView
with RefreshControl
. When the bottom of the outer ScrollView
is reached I can scroll down in the inner SectionList
. But I can never scroll up in the inner SectionList
because of the outer ScrollView
's RefreshControl
.
Same here. Any workaround? i have something like this:
<ScrollView>
<FlatList onEndReach={this.fetchNewPage} />
</ScrollView>
Greetings,
For anyone looking for a workaround for android, I have a solution that was tested successfully.
The core of the fix is to use NestedScollView instead of ScrollView. There's a library that already does this - https://github.com/mohtada-h/react-native-nested-scrollview, however it's not being maintained to support the latest RN.
I've created a patch that can be applied directly from the root of a react native project. You can find the patch file here:
https://raw.githubusercontent.com/hemantasapkota/react-native-nested-scrollview/master/root.patch
Summary of the fix:
npm install react-native-nested-scrollview --save
# From the root of your RN project
wget https://github.com/hemantasapkota/react-native-nested-scrollview/blob/master/root.patch
# Apply the patch
patch -p0 < root.patch | 2>/dev/null
Final step would be to use NestedScrollView instead of ScrollView in your source project.
@hemantasapkota hello dude, i get this error with your solution:
react-native-nested-scrollview\android\src\main\java\com\mohtada\nestedscrollview\ReactNestedScrollViewManager.j
ava:37: error: ReactNestedScrollViewManager is not abstract and does not override abstract method flashScrollIndicators(ReactNestedScrollView) in ScrollComman
dHandler
public class ReactNestedScrollViewManager
package.js
"dependencies": {
"react": "16.0.0-beta.5",
"react-native": "0.49.3",
"react-native-html-parser": "^0.0.5",
"react-native-loading-spinner-overlay": "^0.5.2",
"react-native-navigation": "^1.1.236",
"react-native-nested-scrollview": "^0.0.3",
"react-native-platform-touchable": "^1.1.1",
"react-native-scrollable-tab-view": "^0.8.0",
"react-native-smart-loading-spinner-overlay": "^1.0.2"
},
Hey @dviluk, seems like you're using [react, react-native] => [16.0.0-beta.5, react native 0.49.3] The patch that I posted was for [react, react-native] => [16.0.0-alpha.12, 0.48.4].
Between 0.48.4 & 0.49.3, RN has added a new method to the ScrollCommandHandler interface. To get the build working, use this patch.
--- node_modules/react-native-nested-scrollview/android/src/main/java/com/mohtada/nestedscrollview/ReactNestedScrollViewManager.java
+++ node_modules/react-native-nested-scrollview/android/src/main/java/com/mohtada/nestedscrollview/ReactNestedScrollViewManager.java
@@ -157,6 +157,10 @@ public class ReactNestedScrollViewManager
}
@Override
+ public void flashScrollIndicators(ReactNestedScrollView scrollView) {
+ }
+
+ @Override
public @Nullable Map getExportedCustomDirectEventTypeConstants() {
return createExportedCustomDirectEventTypeConstants();
}
Copy the contents to a file, (example: rn-0.49.3.patch ) and execute the following:
patch -p0 < rn-0.49.3.patch
Hope that's helpful. PS: You'll still need to apply the first patch i posted in the earlier thread.
Hi @hemantasapkota , I tried your patch, but I'm getting this error:
Tried to register two views with the same name AndroidHorizontalScrollView
I'm currently using [react, react-native] => [16.0.0-alpha.12, 0.49.3], I ran both patches by the way.
Thanks for your help dude!
@linoleum First off, probably should use 16.0.0-beta.5 to avoid dependency issues -- unless you still have the React.PropTypes or React.createClass code. Second, If you want to get rid of that error, you can just add a uniqueId function to the AndroidHorizontalScrollView name and it will disappear.
If you guys don't want to maintain huge patches for this on Android, you can instead copy over ReactScrollView.java and ReactScrollViewManager.java from react native and change a few lines (especially if you are in that situation where you can't patch java code when creating builds):
NestedScrollView.class.getDeclaredField("mScroller");
); add your missing imports from the ReactAndroid/.../scroll/ directory; comment out the conflicting onAttachedToWindow
function. RCTNestedScrollView
and import missing dependencies.Also in ScrollView.js, patch the one case where it's a vertical, android scrollview and change it from RCTScrollView
to RCTNestedScrollView
If someone else doesn't do this first, I'm probably going to try and open a PR to support android nested scrollviews and turn it on through a prop or something.
PS: don't forget to add new ReactScrollViewManager()
to your createViewManagers
function
@linoleum00 @kpink224
@ashrithks Awesome example to show how to get this working with android.
The advantage of the NestedScrollView though is that it elegantly handles passing of the responder to the parent if you reach the bounds of the inner scrollview. Plus you don't have to flood the bridge with scroll events or set state every time since it's all happening on the native layer.
@ashrithks i tried example app. It looks awesome. Thanks.
Edit: At our currently developing application, Android setting state very late when you touch nested list. I dont know what is the problem at Android side.
I havenât followed all of the Android related chatter precisely, but if someone is maintaining a native solution that works similar to my iOS one, then we should maybe get in touch and chat about a unified solution that could go into React Native proper. Please ping me if thatâs the case.
cc @kpink224 @ashrithks @hemantasapkota
I can recommend https://github.com/kmagiera/react-native-gesture-handler for a better way to handle multiple ScrollViews on Android.
I have following structure
<Scrollview> (made scrollenbaled =false and refreshcontrol is enabled)
<Scrollview>
horizontal scrolling -- this is working
</Scrollview>
-Tabnavigator
-tab1
-listview (not scrolling)
-tab2
-listview (not scrolling)
</Tabnavigator>
</Scrollview>
I do i make the listview scroll ?
<FlatList
data = {[{}, {}, {}]}
horizontal
pagingEnabled
keyExtractor = {(item, index) => index}
renderItem = {({item, index}) => {
if (index == 0) {
return (
<View style = {{width}}>
<View style = {styles.header}>
<Text>{`Page: ${index}`}</Text>
</View>
<ScrollView
horizontal
pagingEnabled
>
<View style = {styles.header}>
<Text>ChildPage: 1</Text>
</View>
<View style = {styles.header}>
<Text>ChildPage: 2</Text>
</View>
</ScrollView>
</View>
)
} else {
return (
<View style = {styles.header}>
<Text>{`Page: ${index}`}</Text>
</View>
)
}
}}
/>
When scrollview is embedded in a item of flatlist
, it works in iOS, but not working in android.
Any help? thanks
@jenskuhrjorgensen i'm experiencing the same issue!! Did you find any solution?? :(
@rekha110254 Nope - I was so lucky that the design changed instead! đ đ
@chetankotkar i'm experiencing same issue!! Did u find any solution? :/
@ashrithks ur method is buggy, it doesnt work after u scroll down and then go up. tried it on a real device.
Anyone has the solution to this?
I have to use nested FlatList inside ScrollView, but onEndReached keeps firing without reaching the end...
Mine was ScrollView is rendered inside FlatList. The ScrollView scrolling effect doesnt work anymore but the FlatList is working fine.
Any solution for this?
I have found this nestedScrollEnabled but seems like its not working
https://facebook.github.io/react-native/docs/scrollview#nestedscrollenabled
https://snack.expo.io/HJsi9gDmQ
?
nestedScrollEnabled
works, first of all make sure your RN version is >= 0.56.0, because this property was introduced in 0.56.0. Add nestedScrollEnabled
for inner/child ScrollView component.
@ivanzotov its not working.. its all waste of time upgrading to 0.56
As pointed out in earlier comments, now nested scrolling is possible on both platforms via nestedScrollEnabled (which needs to be explicitly set for Android to use it, and as Ivan pointed out you need to have it set in the inner list).
@kelset The OP is not about being able to scroll in nested scrollviews, though, thatâs just something that many people have been discussing here but is off-topic.
Hey @alloy thanks for the clarification, but then I'm confused by:
The developer and user of that component shouldnât need to jump through hoops to have ScrollView callbacks work, but should be able to write isolated components that are unaware of the presence of other ScrollView instances in the view hierarchy and have React Native deal with the details of dispatching events to all components that require them.
I mean, it seems to me that nestedScrollEnabled
obtains that đ€
Or there is a key "under the surface" difference that I'm missing? Was it more about reaching something closer to this?
he suggested abstracting the scroll position/event part from ScrollView into a Scrollable container
Anyway I'll reopen :)
Yeah itâs a nuanced issue đ Itâs about having scrollviews inside a scrollview, which would disable the nested scrollviews from scrolling independently but still receive scroll events _as if_ there were scrolling their own content. If you look at the GIF in the OP, youâll see that per tab thereâs only 1 large scrolling view, but there are actually some nested scrollviews in that hierarchy.
I discussed this issue with @alloy in person. This is a valid proposal and definitely a change we'd like to have in React Native but over the course of almost three years nobody has put themselves forward to actually implement it and we haven't had a need for this at Facebook either, hence I'm going to close this issue.
However, if you feel passionate about adding the right APIs without breaking existing code, please feel free to keep discussing the concrete implementation here and start sending PRs that gets us closer to the goal :)
Try this, It worked for me
Most helpful comment
đ Beautiful issue.
Good job