From 5b2cb47a25b42d6324bafd3750c7c41c2529c608 Mon Sep 17 00:00:00 2001 From: fabriziobertoglio1987 Date: Wed, 6 Jul 2022 12:54:53 +0800 Subject: [PATCH 001/123] draft - invert flatlist without using transform --- Libraries/Lists/VirtualizedList.js | 80 +++++++++---------- .../examples/FlatList/BaseFlatListExample.js | 5 ++ 2 files changed, 41 insertions(+), 44 deletions(-) diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index 69e63090ea32d5..dafd1d226203bd 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -794,7 +794,6 @@ class VirtualizedList extends React.PureComponent { stickyIndicesFromProps: Set, first: number, last: number, - inversionStyle: ViewStyleProp, ) { const { CellRendererComponent, @@ -813,7 +812,7 @@ class VirtualizedList extends React.PureComponent { const end = getItemCount(data) - 1; let prevCellKey; last = Math.min(end, last); - for (let ii = first; ii <= last; ii++) { + const pushCell = ii => { const item = getItem(data, ii); const key = this._keyExtractor(item, ii); this._indicesToKeys.set(ii, key); @@ -831,7 +830,6 @@ class VirtualizedList extends React.PureComponent { getItemLayout={getItemLayout} horizontal={horizontal} index={ii} - inversionStyle={inversionStyle} item={item} key={key} prevCellKey={prevCellKey} @@ -845,6 +843,15 @@ class VirtualizedList extends React.PureComponent { />, ); prevCellKey = key; + }; + if (this.props.inverted) { + for (let ii = last; ii >= first; ii--) { + pushCell(ii); + } + } else { + for (let ii = first; ii <= last; ii++) { + pushCell(ii); + } } } @@ -899,11 +906,6 @@ class VirtualizedList extends React.PureComponent { this.props; const {data, horizontal} = this.props; const isVirtualizationDisabled = this._isVirtualizationDisabled(); - const inversionStyle = this.props.inverted - ? horizontalOrDefault(this.props.horizontal) - ? styles.horizontallyInverted - : styles.verticallyInverted - : null; const cells = []; const stickyIndicesFromProps = new Set(this.props.stickyHeaderIndices); const stickyHeaderIndices = []; @@ -924,10 +926,7 @@ class VirtualizedList extends React.PureComponent { key="$header"> + style={this.props.ListHeaderComponentStyle}> { // $FlowFixMe[incompatible-type] - Typing ReactNativeComponent revealed errors element @@ -945,14 +944,16 @@ class VirtualizedList extends React.PureComponent { ? -1 : initialNumToRenderOrDefault(this.props.initialNumToRender) - 1; const {first, last} = this.state; - this._pushCells( - cells, - stickyHeaderIndices, - stickyIndicesFromProps, - 0, - lastInitialIndex, - inversionStyle, - ); + if (!this.props.inverted) { + this._pushCells( + cells, + stickyHeaderIndices, + stickyIndicesFromProps, + 0, + lastInitialIndex, + ); + this.scrollToOffset({offset: 0, animated: false}); + } const firstAfterInitial = Math.max(lastInitialIndex + 1, first); if (!isVirtualizationDisabled && first > lastInitialIndex + 1) { let insertedStickySpacer = false; @@ -976,7 +977,6 @@ class VirtualizedList extends React.PureComponent { stickyIndicesFromProps, ii, ii, - inversionStyle, ); const trailSpace = this.__getFrameMetricsApprox(first).offset - @@ -1005,8 +1005,17 @@ class VirtualizedList extends React.PureComponent { stickyIndicesFromProps, firstAfterInitial, last, - inversionStyle, ); + if (this.props.inverted) { + this._pushCells( + cells, + stickyHeaderIndices, + stickyIndicesFromProps, + 0, + lastInitialIndex, + ); + this.scrollToEnd({animated: false}); + } if (!this._hasWarned.keys && _usedIndexForKey) { console.warn( 'VirtualizedList: missing keys for items, make sure to specify a key or id property on each ' + @@ -1051,7 +1060,7 @@ class VirtualizedList extends React.PureComponent { element.props.onLayout(event); } }, - style: StyleSheet.compose(inversionStyle, element.props.style), + style: element.props.style, }), ); } @@ -1069,10 +1078,7 @@ class VirtualizedList extends React.PureComponent { key="$footer"> + style={this.props.ListFooterComponentStyle}> { // $FlowFixMe[incompatible-type] - Typing ReactNativeComponent revealed errors element @@ -1098,9 +1104,7 @@ class VirtualizedList extends React.PureComponent { ? this.props.invertStickyHeaders : this.props.inverted, stickyHeaderIndices, - style: inversionStyle - ? [inversionStyle, this.props.style] - : this.props.style, + style: this.props.style, }; this._hasMore = @@ -1952,7 +1956,6 @@ type CellRendererProps = { }, horizontal: ?boolean, index: number, - inversionStyle: ViewStyleProp, item: Item, // This is extracted by ScrollViewStickyHeader onCellLayout: (event: Object, cellKey: string, index: number) => void, @@ -2089,7 +2092,6 @@ class CellRenderer extends React.Component< horizontal, item, index, - inversionStyle, renderItem, } = this.props; const element = this._renderElement( @@ -2111,26 +2113,16 @@ class CellRenderer extends React.Component< : ItemSeparatorComponent && ( ); - const cellStyle = inversionStyle - ? horizontal - ? [styles.rowReverse, inversionStyle] - : [styles.columnReverse, inversionStyle] - : horizontal - ? [styles.row, inversionStyle] - : inversionStyle; const result = !CellRendererComponent ? ( /* $FlowFixMe[incompatible-type-arg] (>=0.89.0 site=react_native_fb) * This comment suppresses an error found when Flow v0.89 was deployed. * To see the error, delete this comment and run Flow. */ - + {element} {itemSeparator} ) : ( - + {element} {itemSeparator} diff --git a/packages/rn-tester/js/examples/FlatList/BaseFlatListExample.js b/packages/rn-tester/js/examples/FlatList/BaseFlatListExample.js index 211706ced3c8b5..fd60453915d464 100644 --- a/packages/rn-tester/js/examples/FlatList/BaseFlatListExample.js +++ b/packages/rn-tester/js/examples/FlatList/BaseFlatListExample.js @@ -32,6 +32,11 @@ const DATA = [ 'Beer', 'Cheesecake', 'Ice Cream', + 'Spaghetti Carbonara', + 'Margherita', + 'Diavola', + 'Ravioli Mamma Mia', + 'Bresaola', ]; const Item = ({item, separators}: RenderItemProps) => { From 06fb1dd587b03556650086c1d6e4012de9315622 Mon Sep 17 00:00:00 2001 From: fabriziobertoglio1987 Date: Wed, 6 Jul 2022 13:06:53 +0800 Subject: [PATCH 002/123] adding comments to scroll to top/bottom optimization --- Libraries/Lists/VirtualizedList.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index dafd1d226203bd..e2abd1a1738607 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -944,6 +944,7 @@ class VirtualizedList extends React.PureComponent { ? -1 : initialNumToRenderOrDefault(this.props.initialNumToRender) - 1; const {first, last} = this.state; + // scroll to top optimization. The first page is always rendered. if (!this.props.inverted) { this._pushCells( cells, @@ -1006,6 +1007,7 @@ class VirtualizedList extends React.PureComponent { firstAfterInitial, last, ); + // scroll to bottom optimization. The last page is always rendered in an inverted flatlist. if (this.props.inverted) { this._pushCells( cells, From f72cac20fc540ff4c29b7f8a92e1b2abcf958118 Mon Sep 17 00:00:00 2001 From: fabriziobertoglio1987 Date: Fri, 8 Jul 2022 09:20:23 +0800 Subject: [PATCH 003/123] draft --- Libraries/Lists/FlatList.js | 2 ++ Libraries/Lists/VirtualizedList.js | 28 +++++++++++++------ .../scroll/ReactHorizontalScrollView.java | 1 + .../examples/FlatList/FlatListExampleIndex.js | 2 ++ 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Libraries/Lists/FlatList.js b/Libraries/Lists/FlatList.js index a56962691c09c9..288aa8d7a99e77 100644 --- a/Libraries/Lists/FlatList.js +++ b/Libraries/Lists/FlatList.js @@ -348,6 +348,7 @@ class FlatList extends React.PureComponent, void> { * Check out [scrollToOffset](docs/virtualizedlist.html#scrolltooffset) of VirtualizedList */ scrollToOffset(params: {animated?: ?boolean, offset: number, ...}) { + console.log('TESTING::FlatList scrollToOffset'); if (this._listRef) { this._listRef.scrollToOffset(params); } @@ -434,6 +435,7 @@ class FlatList extends React.PureComponent, void> { } componentDidUpdate(prevProps: Props) { + console.log('TESTING::FlatList componentDidUpdate'); invariant( prevProps.numColumns === this.props.numColumns, 'Changing numColumns on the fly is not supported. Change the key prop on FlatList when ' + diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index e2abd1a1738607..add45515c9089c 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -388,13 +388,14 @@ class VirtualizedList extends React.PureComponent { const animated = params ? params.animated : true; const veryLast = this.props.getItemCount(this.props.data) - 1; const frame = this.__getFrameMetricsApprox(veryLast); - const offset = Math.max( - 0, + console.log('TESTING::VirtualizedList scrollToEnd'); + console.log('TESTING::VirtualizedList frame', frame); + const offsetCalculation = frame.offset + - frame.length + - this._footerLength - - this._scrollMetrics.visibleLength, - ); + frame.length + + this._footerLength - + this._scrollMetrics.visibleLength; + const offset = Math.max(0, offsetCalculation); if (this._scrollRef == null) { return; @@ -751,6 +752,17 @@ class VirtualizedList extends React.PureComponent { parentDebugInfo: this.context.debugInfo, }); } + if (this.props.inverted) { + console.log(''); + // this.scrollToEnd({animated: false}); + // const veryLast = this.props.getItemCount(this.props.data) - 1; + const veryLast = 4; + const frame = this.__getFrameMetricsApprox(veryLast); + this.scrollToOffset({offset: frame.offset, animated: true}); + console.log('TESTING::VirtualizedList veryLast', veryLast); + console.log('TESTING::VirtualizedList frame', frame); + // this.scrollToOffset({offset: 310, animated: true}); + } } componentWillUnmount() { @@ -953,7 +965,7 @@ class VirtualizedList extends React.PureComponent { 0, lastInitialIndex, ); - this.scrollToOffset({offset: 0, animated: false}); + // this.scrollToOffset({offset: 0, animated: false}); } const firstAfterInitial = Math.max(lastInitialIndex + 1, first); if (!isVirtualizationDisabled && first > lastInitialIndex + 1) { @@ -1016,7 +1028,7 @@ class VirtualizedList extends React.PureComponent { 0, lastInitialIndex, ); - this.scrollToEnd({animated: false}); + // this.scrollToOffset({offset: 1300, animated: true}); } if (!this._hasWarned.keys && _usedIndexForKey) { console.warn( diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java index 964c8d721ba8ff..fc3dd626ae19d9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java @@ -127,6 +127,7 @@ public ReactHorizontalScrollView(Context context, @Nullable FpsListener fpsListe I18nUtil.getInstance().isRTL(context) ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR); + fullScroll(View.FOCUS_DOWN); } public boolean getScrollEnabled() { diff --git a/packages/rn-tester/js/examples/FlatList/FlatListExampleIndex.js b/packages/rn-tester/js/examples/FlatList/FlatListExampleIndex.js index 7bcfa14d7acc62..2fc61e2f55fbb1 100644 --- a/packages/rn-tester/js/examples/FlatList/FlatListExampleIndex.js +++ b/packages/rn-tester/js/examples/FlatList/FlatListExampleIndex.js @@ -17,6 +17,7 @@ import onViewableItemsChangedExample from './FlatList-onViewableItemsChanged'; import WithSeparatorsExample from './FlatList-withSeparators'; import MultiColumnExample from './FlatList-multiColumn'; import StickyHeadersExample from './FlatList-stickyHeaders'; +import NestedExample from './FlatList-nested'; export default ({ framework: 'React', @@ -34,5 +35,6 @@ export default ({ WithSeparatorsExample, MultiColumnExample, StickyHeadersExample, + NestedExample, ], }: RNTesterModule); From e53aa54c1ebcc7a90f4fa71010ed89476e935ca3 Mon Sep 17 00:00:00 2001 From: fabriziobertoglio1987 Date: Fri, 8 Jul 2022 12:40:50 +0800 Subject: [PATCH 004/123] workaround scrollToEnd issues for inverted flatlist addresses issues explained in https://github.com/facebook/react-native/issues/30373#issuecomment-1176199466 handles cases when adding new item to inverted flatlist (flatlist has to scroll up to the new item) test cases https://github.com/facebook/react-native/issues/30373#issuecomment-1178527848 --- Libraries/Lists/VirtualizedList.js | 47 +++++++++++++++++------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index add45515c9089c..e64c8c5291f82d 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -388,14 +388,13 @@ class VirtualizedList extends React.PureComponent { const animated = params ? params.animated : true; const veryLast = this.props.getItemCount(this.props.data) - 1; const frame = this.__getFrameMetricsApprox(veryLast); - console.log('TESTING::VirtualizedList scrollToEnd'); - console.log('TESTING::VirtualizedList frame', frame); - const offsetCalculation = + const offset = Math.max( + 0, frame.offset + - frame.length + - this._footerLength - - this._scrollMetrics.visibleLength; - const offset = Math.max(0, offsetCalculation); + frame.length + + this._footerLength - + this._scrollMetrics.visibleLength, + ); if (this._scrollRef == null) { return; @@ -752,17 +751,6 @@ class VirtualizedList extends React.PureComponent { parentDebugInfo: this.context.debugInfo, }); } - if (this.props.inverted) { - console.log(''); - // this.scrollToEnd({animated: false}); - // const veryLast = this.props.getItemCount(this.props.data) - 1; - const veryLast = 4; - const frame = this.__getFrameMetricsApprox(veryLast); - this.scrollToOffset({offset: frame.offset, animated: true}); - console.log('TESTING::VirtualizedList veryLast', veryLast); - console.log('TESTING::VirtualizedList frame', frame); - // this.scrollToOffset({offset: 310, animated: true}); - } } componentWillUnmount() { @@ -965,7 +953,6 @@ class VirtualizedList extends React.PureComponent { 0, lastInitialIndex, ); - // this.scrollToOffset({offset: 0, animated: false}); } const firstAfterInitial = Math.max(lastInitialIndex + 1, first); if (!isVirtualizationDisabled && first > lastInitialIndex + 1) { @@ -1028,7 +1015,6 @@ class VirtualizedList extends React.PureComponent { 0, lastInitialIndex, ); - // this.scrollToOffset({offset: 1300, animated: true}); } if (!this._hasWarned.keys && _usedIndexForKey) { console.warn( @@ -1589,6 +1575,27 @@ class VirtualizedList extends React.PureComponent { this.props.onContentSizeChange(width, height); } this._scrollMetrics.contentLength = this._selectLength({height, width}); + if ( + this._hasTriggeredInitialScrollToIndex && + this.props.initialScrollIndex == null && + this.props.inverted + ) { + this.scrollToOffset({ + animated: false, + offset: 0, + }); + } + if ( + !this._hasTriggeredInitialScrollToIndex && + this.props.initialScrollIndex == null && + this.props.inverted + ) { + this.scrollToOffset({ + animated: false, + offset: this._scrollMetrics.contentLength, + }); + this._hasTriggeredInitialScrollToIndex = true; + } this._scheduleCellsToRenderUpdate(); this._maybeCallOnEndReached(); }; From 4c08d7cafed982571ba6e6b355f855f18c4cf23f Mon Sep 17 00:00:00 2001 From: fabriziobertoglio1987 Date: Fri, 8 Jul 2022 12:48:19 +0800 Subject: [PATCH 005/123] adding flatlist nested example --- Libraries/Lists/FlatList.js | 2 - .../js/examples/FlatList/FlatList-nested.js | 113 ++++++++++++++++++ 2 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 packages/rn-tester/js/examples/FlatList/FlatList-nested.js diff --git a/Libraries/Lists/FlatList.js b/Libraries/Lists/FlatList.js index 288aa8d7a99e77..a56962691c09c9 100644 --- a/Libraries/Lists/FlatList.js +++ b/Libraries/Lists/FlatList.js @@ -348,7 +348,6 @@ class FlatList extends React.PureComponent, void> { * Check out [scrollToOffset](docs/virtualizedlist.html#scrolltooffset) of VirtualizedList */ scrollToOffset(params: {animated?: ?boolean, offset: number, ...}) { - console.log('TESTING::FlatList scrollToOffset'); if (this._listRef) { this._listRef.scrollToOffset(params); } @@ -435,7 +434,6 @@ class FlatList extends React.PureComponent, void> { } componentDidUpdate(prevProps: Props) { - console.log('TESTING::FlatList componentDidUpdate'); invariant( prevProps.numColumns === this.props.numColumns, 'Changing numColumns on the fly is not supported. Change the key prop on FlatList when ' + diff --git a/packages/rn-tester/js/examples/FlatList/FlatList-nested.js b/packages/rn-tester/js/examples/FlatList/FlatList-nested.js new file mode 100644 index 00000000000000..8eb622b2ffbb3f --- /dev/null +++ b/packages/rn-tester/js/examples/FlatList/FlatList-nested.js @@ -0,0 +1,113 @@ +import React from "react"; +import { + SafeAreaView, + View, + FlatList, + StyleSheet, + Text, + StatusBar, + Button, +} from "react-native"; + +const DATA = [ + { + id: "bd7acbea-c1b1-46c2-aed5-3ad53abb28ba", + title: "First Item", + }, + { + id: "3ac68afc-c605-48d3-a4f8-fbd91aa97f63", + title: "Second Item", + }, + { + id: "58694a0f-3da1-471f-bd96-145571e29d72", + title: "Third Item", + }, + { + id: "bd7acbea-c1b1-46c2-aed5-3ad53abb8bbb", + title: "Fourth Item", + }, + { + id: "3ac68afc-c605-48d3-a4f8-fbd91aa97676", + title: "Fifth Item", + }, + { + id: "58694a0f-3da1-471f-bd96-145571e27234", + title: "Sixth Item", + }, + { + id: "58694a0f-3da1-471f-bd96-145571e29234", + title: "Seven Item", + }, + { + id: "58694a0f-3da1-471f-bd96-145571429234", + title: "Eight Item", + }, + { + id: "58694a0f-3da1-471f-bd96-115571429234", + title: "Nine Item", + }, + { + id: "58694a0f-3da1-471f-bd96-1155h1429234", + title: "Ten Item", + }, +]; + +const Item = ({ title }) => ( + {title} +); + +const renderItem = ({ item }) => ; +const ITEM_HEIGHT = 50; + +const renderFlatList = ({ item }) => ( + +); + +function NestedFlatList(props) { + const [items, addItem] = React.useState(DATA); + return ( + +