-
Notifications
You must be signed in to change notification settings - Fork 25.1k
Description
For Discussion
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.
Examples
Contrived
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.
Real-world
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:
Proof of Concept
For my app I wrote some 🐒-patches that do the following:
- When a
RCTScrollViewis added to a view hierarchy, it registers for aRCTScrollEventnotification. - When a
RCTScrollViewreceives a native scroll callback it first does its own JS scroll event dispatching and afterwards it posts aRCTScrollEventnotification for any childRCTScrollViewinstances to pick-up. - The child
RCTScrollViewinstance creates a newRCTScrollEventobject that offsets the scrollview’scontentOffsetto reflect its location inside the parentRCTScrollView. - The child
RCTScrollViewthen dispatches the JS scroll event as normal and the component doesn’t know any better than that it has scrolled as normal.
Notes:
- Please don’t judge my implementation as anything other than a POC of the high-level functionality.
- I used
NSNotificationCenterbecause 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 theUIScrollViewdelegate that can dispatch events to multiple delegates (example). - While I have not yet used the callbacks for a
ListViewcomponent in the current version of our app, I have tested and verified that theonEndReachedcallback gets triggered, thus I see no reason why theremoveClippedSubviewsoptimisation wouldn’t work either. At least that’s my goal, as I will need this soon.
Discussion
- I’m looking for suggestions as to how this should be implemented for it to be acceptable in React Native.
- When I discussed this with @javache offline he suggested abstracting the scroll position/event part from
ScrollViewinto aScrollablecontainer. That container could then create theScrollViewif not inside a parentScrollView. - I want to spend the time to write the iOS implementation.
- I currently do not target Android and thus don’t feel like I’m the right person to implement it for that platform at the moment, at least not from the get-go.
