-
Notifications
You must be signed in to change notification settings - Fork 24.4k
Commit
Summary: This adds support for both automagical sticky section headers in `SectionList` as well as the more free-form `stickyHeaderIndices` on `FlatList` or `VirtualizedList`. The basic concept is to take the initial `stickySectionHeaders` and remap them to the indices corresponding to the mounted subset in the render window. The main trick here is that the currently stuck header might itself be outside of the render window, so we need to search the gap to see if that's the case and render it (with spacers above and below it instead of one big spacer). In the `SectionList` we simply pre-compute the sticky headers at the same time as when we scan the sections to determine the flattened length and pass those to `VirtualizedList`. This also requires some updates to `ScrollView` to work in the churny environment of `VirtualizedList`. We propogate the keys on the children to the animated wrappers so that as items are removed and the indices of the remaining items change, react can keep proper track of them. We also fix the scroll back case where new headers are rendered from the top down and aren't updated with the `setNextLayoutY` callback because the `onLayout` call for the next header happened before it was mounted. This is done by just tracking all the layout values in a map and providing them to the sticky components at render time. This might also improve perf a little by property configuring the animations syncronously instead of waiting for the `onLayout` callback. We also need to protect against stale onLayout callbacks and other fun stuff. == Test Plan == https://www.facebook.com/groups/react.native.community/permalink/940332509435661/ Scroll a lot with and without debug mode on. Make sure spinner still spins and there are no crashes (lots of crashes during development due to the animated configuration being non-monotonic if anything stale values get through). Also made sure that tapping a row to change it's height would properly update the animation configurations so the collision point would still be correct. Reviewed By: yungsters Differential Revision: D4695065 fbshipit-source-id: 855c4e31c8f8b450d32150dbdb2e07f1a9f9f98e
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,7 @@ | |
'use strict'; | ||
|
||
const MetroListView = require('MetroListView'); | ||
const Platform = require('Platform'); | ||
const React = require('React'); | ||
const VirtualizedSectionList = require('VirtualizedSectionList'); | ||
|
||
|
@@ -52,9 +53,7 @@ type SectionBase<SectionItemT> = { | |
keyExtractor?: (item: SectionItemT) => string, | ||
|
||
// TODO: support more optional/override props | ||
// FooterComponent?: ?ReactClass<*>, | ||
// HeaderComponent?: ?ReactClass<*>, | ||
// onViewableItemsChanged?: ({viewableItems: Array<ViewToken>, changed: Array<ViewToken>}) => void, | ||
// onViewableItemsChanged?: ... | ||
}; | ||
|
||
type RequiredProps<SectionT: SectionBase<any>> = { | ||
|
@@ -102,7 +101,10 @@ type OptionalProps<SectionT: SectionBase<any>> = { | |
* Called when the viewability of rows changes, as defined by the | ||
* `viewabilityConfig` prop. | ||
*/ | ||
onViewableItemsChanged?: ?(info: {viewableItems: Array<ViewToken>, changed: Array<ViewToken>}) => void, | ||
onViewableItemsChanged?: ?(info: { | ||
viewableItems: Array<ViewToken>, | ||
changed: Array<ViewToken>, | ||
}) => void, | ||
/** | ||
* Set this true while waiting for new data from a refresh. | ||
*/ | ||
|
@@ -114,13 +116,23 @@ type OptionalProps<SectionT: SectionBase<any>> = { | |
prevProps: {item: Item, index: number}, | ||
nextProps: {item: Item, index: number} | ||
) => boolean, | ||
/** | ||
* Makes section headers stick to the top of the screen until the next one pushes it off. Only | ||
* enabled by default on iOS because that is the platform standard there. | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
sahrens
Author
Contributor
|
||
*/ | ||
stickySectionHeadersEnabled?: boolean, | ||
}; | ||
|
||
type Props<SectionT> = RequiredProps<SectionT> | ||
& OptionalProps<SectionT> | ||
& VirtualizedSectionListProps<SectionT>; | ||
|
||
type DefaultProps = typeof VirtualizedSectionList.defaultProps; | ||
const defaultProps = { | ||
...VirtualizedSectionList.defaultProps, | ||
stickySectionHeadersEnabled: Platform.OS === 'ios', | ||
}; | ||
|
||
type DefaultProps = typeof defaultProps; | ||
|
||
/** | ||
* A performant interface for rendering sectioned lists, supporting the most handy features: | ||
|
@@ -136,7 +148,8 @@ type DefaultProps = typeof VirtualizedSectionList.defaultProps; | |
* - Pull to Refresh. | ||
* - Scroll loading. | ||
* | ||
* If you don't need section support and want a simpler interface, use [`<FlatList>`](/react-native/docs/flatlist.html). | ||
* If you don't need section support and want a simpler interface, use | ||
* [`<FlatList>`](/react-native/docs/flatlist.html). | ||
* | ||
* If you need _sticky_ section header support, use `ListView` for now. | ||
* | ||
|
@@ -180,7 +193,7 @@ class SectionList<SectionT: SectionBase<any>> | |
extends React.PureComponent<DefaultProps, Props<SectionT>, void> | ||
{ | ||
props: Props<SectionT>; | ||
static defaultProps: DefaultProps = VirtualizedSectionList.defaultProps; | ||
static defaultProps: DefaultProps = defaultProps; | ||
|
||
render() { | ||
const List = this.props.legacyImplementation ? MetroListView : VirtualizedSectionList; | ||
|
7 comments
on commit 72670bf
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.
🎉 🎉 🎉
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.
👍
Can't wait to test it on UIExplorer, unfortunately it still doesn't work well on Android, while pretty good on iOS
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.
@sahrens any plan to look into the stickySectionHeader issue on Android? You can see the SectionList
example in UIExplorer
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.
What problems are you seeing on Android exactly? @janicduplessis is the master there.
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.
Just tested and there are issues on Android, some headers just disappear. Might be an issue with view clipping or z-index.
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.
It works well in the ListView paging example so I guess it has to do with FlatList removing / adding views and z-index (view clipping is disabled on android anyway).
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.
Here's the fix for android bugs #13105
@sahrens Just throwing this out here but what about we make sticky section headers the same for iOS and Android here (either enabled or disabled)? For ListView I preferred the different values for backwards compatibility but here I kind of prefer consistency.