@@ -314,6 +314,12 @@ type State = {
314
314
last : number ,
315
315
} ;
316
316
317
+ type CachedCell = {
318
+ component : any ,
319
+ deps : Array < any > ,
320
+ used ?: boolean ,
321
+ } ;
322
+
317
323
/**
318
324
* Default Props Helper Functions
319
325
* Use the following helper functions for default values
@@ -784,8 +790,51 @@ class VirtualizedList extends React.PureComponent<Props, State> {
784
790
} ;
785
791
}
786
792
793
+ _cachedCells: Map < string , CachedCell > = new Map < string , CachedCell > ( ) ;
794
+ _cells = [ ] ;
795
+
796
+ _markCellsAsUnused ( ) {
797
+ this . _cachedCells . forEach ( cell => {
798
+ cell . used = false ;
799
+ } ) ;
800
+ }
801
+
802
+ _deleteUnusedCells ( ) {
803
+ this . _cachedCells . forEach ( ( cell , key ) => {
804
+ if ( ! cell . used ) {
805
+ this . _cachedCells . delete ( key ) ;
806
+ }
807
+ } ) ;
808
+ }
809
+
810
+ _pushCell ( key , deps , creator ) {
811
+ //$FlowFixMe[incompatible-type]
812
+ let cell : CachedCell = this . _cachedCells . get ( key ) ;
813
+ let depsChanged = false ;
814
+ if ( ! cell || cell . deps . length !== deps . length ) {
815
+ depsChanged = true ;
816
+ } else {
817
+ for ( let i = 0 ; i < deps . length ; i ++ ) {
818
+ if ( cell . deps [ i ] !== deps [ i ] ) {
819
+ depsChanged = true ;
820
+ break ;
821
+ }
822
+ }
823
+ }
824
+ if ( depsChanged ) {
825
+ this . _cachedCells . set (
826
+ key ,
827
+ ( cell = {
828
+ component : creator ( ) ,
829
+ deps,
830
+ } ) ,
831
+ ) ;
832
+ }
833
+ cell . used = true ;
834
+ this . _cells . push ( cell . component ) ;
835
+ }
836
+
787
837
_pushCells (
788
- cells : Array < Object > ,
789
838
stickyHeaderIndices : Array < number > ,
790
839
stickyIndicesFromProps : Set < number > ,
791
840
first : number ,
@@ -809,9 +858,19 @@ class VirtualizedList extends React.PureComponent<Props, State> {
809
858
const key = this . _keyExtractor ( item , ii ) ;
810
859
this . _indicesToKeys . set ( ii , key ) ;
811
860
if ( stickyIndicesFromProps . has ( ii + stickyOffset ) ) {
812
- stickyHeaderIndices . push ( cells . length ) ;
861
+ stickyHeaderIndices . push ( this . _cells . length ) ;
813
862
}
814
- cells . push (
863
+ const deps = [
864
+ CellRendererComponent ,
865
+ ItemSeparatorComponent ,
866
+ horizontal ,
867
+ ii ,
868
+ inversionStyle ,
869
+ item ,
870
+ prevCellKey ,
871
+ this . props ,
872
+ ] ;
873
+ this . _pushCell ( key , deps , ( ) => (
815
874
< CellRenderer
816
875
CellRendererComponent = { CellRendererComponent }
817
876
ItemSeparatorComponent = { ii < end ? ItemSeparatorComponent : undefined }
@@ -830,8 +889,8 @@ class VirtualizedList extends React.PureComponent<Props, State> {
830
889
ref = { ref => {
831
890
this . _cellRefs [ key ] = ref ;
832
891
} }
833
- /> ,
834
- ) ;
892
+ />
893
+ ) ) ;
835
894
prevCellKey = key ;
836
895
}
837
896
}
@@ -895,37 +954,40 @@ class VirtualizedList extends React.PureComponent<Props, State> {
895
954
? styles . horizontallyInverted
896
955
: styles . verticallyInverted
897
956
: null ;
898
- const cells = [ ] ;
957
+ this . _cells = [ ] ;
958
+ this . _markCellsAsUnused ( ) ;
899
959
const stickyIndicesFromProps = new Set ( this . props . stickyHeaderIndices ) ;
900
960
const stickyHeaderIndices = [ ] ;
901
961
if ( ListHeaderComponent ) {
902
962
if ( stickyIndicesFromProps . has ( 0 ) ) {
903
963
stickyHeaderIndices . push ( 0 ) ;
904
964
}
905
- const element = React . isValidElement ( ListHeaderComponent ) ? (
906
- ListHeaderComponent
907
- ) : (
908
- // $FlowFixMe[not-a-component]
909
- // $FlowFixMe[incompatible-type-arg]
910
- < ListHeaderComponent />
911
- ) ;
912
- cells . push (
913
- < VirtualizedListCellContextProvider
914
- cellKey = { this . _getCellKey ( ) + '-header' }
915
- key = "$header" >
916
- < View
917
- onLayout = { this . _onLayoutHeader }
918
- style = { StyleSheet . compose (
919
- inversionStyle ,
920
- this . props . ListHeaderComponentStyle ,
921
- ) } >
922
- {
923
- // $FlowFixMe[incompatible-type] - Typing ReactNativeComponent revealed errors
924
- element
925
- }
926
- </ View >
927
- </ VirtualizedListCellContextProvider > ,
928
- ) ;
965
+ this . _pushCell ( '$header' , [ ] , ( ) => {
966
+ const element = React . isValidElement ( ListHeaderComponent ) ? (
967
+ ListHeaderComponent
968
+ ) : (
969
+ // $FlowFixMe[not-a-component]
970
+ // $FlowFixMe[incompatible-type-arg]
971
+ < ListHeaderComponent />
972
+ ) ;
973
+ return (
974
+ < VirtualizedListCellContextProvider
975
+ cellKey = { this . _getCellKey ( ) + '-header' }
976
+ key = "$header" >
977
+ < View
978
+ onLayout = { this . _onLayoutHeader }
979
+ style = { StyleSheet . compose (
980
+ inversionStyle ,
981
+ this . props . ListHeaderComponentStyle ,
982
+ ) } >
983
+ {
984
+ // $FlowFixMe[incompatible-type] - Typing ReactNativeComponent revealed errors
985
+ element
986
+ }
987
+ </ View >
988
+ </ VirtualizedListCellContextProvider >
989
+ ) ;
990
+ } ) ;
929
991
}
930
992
const itemCount = this . props . getItemCount ( data ) ;
931
993
if ( itemCount > 0 ) {
@@ -937,7 +999,6 @@ class VirtualizedList extends React.PureComponent<Props, State> {
937
999
: initialNumToRenderOrDefault ( this . props . initialNumToRender ) - 1 ;
938
1000
const { first, last} = this . state ;
939
1001
this . _pushCells (
940
- cells ,
941
1002
stickyHeaderIndices ,
942
1003
stickyIndicesFromProps ,
943
1004
0 ,
@@ -958,11 +1019,10 @@ class VirtualizedList extends React.PureComponent<Props, State> {
958
1019
stickyBlock . offset -
959
1020
initBlock . offset -
960
1021
( this . props . initialScrollIndex ? 0 : initBlock . length ) ;
961
- cells . push (
1022
+ this . _cells . push (
962
1023
< View key = "$sticky_lead" style = { { [ spacerKey ] : leadSpace } } /> ,
963
1024
) ;
964
1025
this . _pushCells (
965
- cells ,
966
1026
stickyHeaderIndices ,
967
1027
stickyIndicesFromProps ,
968
1028
ii ,
@@ -972,7 +1032,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
972
1032
const trailSpace =
973
1033
this . _getFrameMetricsApprox ( first ) . offset -
974
1034
( stickyBlock . offset + stickyBlock . length ) ;
975
- cells . push (
1035
+ this . _cells . push (
976
1036
< View key = "$sticky_trail" style = { { [ spacerKey ] : trailSpace } } /> ,
977
1037
) ;
978
1038
insertedStickySpacer = true ;
@@ -985,13 +1045,12 @@ class VirtualizedList extends React.PureComponent<Props, State> {
985
1045
const firstSpace =
986
1046
this . _getFrameMetricsApprox ( first ) . offset -
987
1047
( initBlock . offset + initBlock . length ) ;
988
- cells . push (
1048
+ this . _cells . push (
989
1049
< View key = "$lead_spacer" style = { { [ spacerKey ] : firstSpace } } /> ,
990
1050
) ;
991
1051
}
992
1052
}
993
1053
this . _pushCells (
994
- cells ,
995
1054
stickyHeaderIndices ,
996
1055
stickyIndicesFromProps ,
997
1056
firstAfterInitial ,
@@ -1019,22 +1078,22 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1019
1078
endFrame . offset +
1020
1079
endFrame . length -
1021
1080
( lastFrame . offset + lastFrame . length ) ;
1022
- cells . push (
1081
+ this . _cells . push (
1023
1082
< View key = "$tail_spacer" style = { { [ spacerKey ] : tailSpacerLength } } /> ,
1024
1083
) ;
1025
1084
}
1026
1085
} else if ( ListEmptyComponent ) {
1027
- const element : React . Element < any > = ((React.isValidElement(
1028
- ListEmptyComponent,
1029
- ) ? (
1030
- ListEmptyComponent
1031
- ) : (
1032
- // $FlowFixMe[not-a-component]
1033
- // $FlowFixMe[incompatible-type-arg ]
1034
- < ListEmptyComponent />
1035
- )): any);
1036
- cells.push(
1037
- React.cloneElement(element, {
1086
+ this . _pushCell ( '$empty' , [ ] , ( ) => {
1087
+ const element : React . Element < any > = ((React.isValidElement(
1088
+ ListEmptyComponent,
1089
+ ) ? (
1090
+ ListEmptyComponent
1091
+ ) : (
1092
+ // $FlowFixMe[not-a-component ]
1093
+ // $FlowFixMe[incompatible-type-arg]
1094
+ < ListEmptyComponent />
1095
+ )): any);
1096
+ return React.cloneElement(element, {
1038
1097
key : '$empty' ,
1039
1098
onLayout : event => {
1040
1099
this . _onLayoutEmpty ( event ) ;
@@ -1043,34 +1102,36 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1043
1102
}
1044
1103
} ,
1045
1104
style : StyleSheet . compose ( inversionStyle , element . props . style ) ,
1046
- } ),
1047
- );
1105
+ } );
1106
+ } ) ;
1048
1107
}
1049
1108
if ( ListFooterComponent ) {
1050
- const element = React . isValidElement ( ListFooterComponent ) ? (
1051
- ListFooterComponent
1052
- ) : (
1053
- // $FlowFixMe[not-a-component]
1054
- // $FlowFixMe[incompatible-type-arg]
1055
- < ListFooterComponent />
1056
- ) ;
1057
- cells . push (
1058
- < VirtualizedListCellContextProvider
1059
- cellKey = { this . _getFooterCellKey ( ) }
1060
- key = "$footer" >
1061
- < View
1062
- onLayout = { this . _onLayoutFooter }
1063
- style = { StyleSheet . compose (
1064
- inversionStyle ,
1065
- this . props . ListFooterComponentStyle ,
1066
- ) } >
1067
- {
1068
- // $FlowFixMe[incompatible-type] - Typing ReactNativeComponent revealed errors
1069
- element
1070
- }
1071
- </ View >
1072
- </ VirtualizedListCellContextProvider > ,
1073
- ) ;
1109
+ this . _pushCell ( '$footer' , [ ] , ( ) => {
1110
+ const element = React . isValidElement ( ListFooterComponent ) ? (
1111
+ ListFooterComponent
1112
+ ) : (
1113
+ // $FlowFixMe[not-a-component]
1114
+ // $FlowFixMe[incompatible-type-arg]
1115
+ < ListFooterComponent />
1116
+ ) ;
1117
+ return (
1118
+ < VirtualizedListCellContextProvider
1119
+ cellKey = { this . _getFooterCellKey ( ) }
1120
+ key = "$footer" >
1121
+ < View
1122
+ onLayout = { this . _onLayoutFooter }
1123
+ style = { StyleSheet . compose (
1124
+ inversionStyle ,
1125
+ this . props . ListFooterComponentStyle ,
1126
+ ) } >
1127
+ {
1128
+ // $FlowFixMe[incompatible-type] - Typing ReactNativeComponent revealed errors
1129
+ element
1130
+ }
1131
+ </ View >
1132
+ </ VirtualizedListCellContextProvider >
1133
+ ) ;
1134
+ } ) ;
1074
1135
}
1075
1136
const scrollProps = {
1076
1137
...this . props ,
@@ -1117,11 +1178,12 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1117
1178
{
1118
1179
ref : this . _captureScrollRef ,
1119
1180
} ,
1120
- cells ,
1181
+ this . _cells ,
1121
1182
) }
1122
1183
</ VirtualizedListContextProvider >
1123
1184
) ;
1124
1185
let ret = innerRet ;
1186
+ this . _deleteUnusedCells ( ) ;
1125
1187
if ( __DEV__ ) {
1126
1188
ret = (
1127
1189
< ScrollView . Context . Consumer >
@@ -1931,33 +1993,27 @@ type CellRendererProps = {
1931
1993
} ;
1932
1994
1933
1995
type CellRendererState = {
1934
- separatorProps : $ReadOnly < { |
1935
- highlighted : boolean ,
1936
- leadingItem : ?Item ,
1937
- | } > ,
1996
+ highlighted : boolean ,
1997
+ leadingItem : ?Item ,
1938
1998
...
1939
1999
} ;
1940
2000
1941
- class CellRenderer extends React . Component <
2001
+ class CellRenderer extends React . PureComponent <
1942
2002
CellRendererProps ,
1943
2003
CellRendererState,
1944
2004
> {
1945
2005
state = {
1946
- separatorProps : {
1947
- highlighted : false ,
1948
- leadingItem : this . props . item ,
1949
- } ,
2006
+ highlighted : false ,
2007
+ leadingItem : this . props . item ,
1950
2008
} ;
1951
2009
1952
2010
static getDerivedStateFromProps (
1953
2011
props : CellRendererProps ,
1954
2012
prevState : CellRendererState ,
1955
2013
) : ?CellRendererState {
1956
2014
return {
1957
- separatorProps : {
1958
- ...prevState . separatorProps ,
1959
- leadingItem : props . item ,
1960
- } ,
2015
+ ...prevState ,
2016
+ leadingItem : props . item ,
1961
2017
} ;
1962
2018
}
1963
2019
@@ -1987,7 +2043,8 @@ class CellRenderer extends React.Component<
1987
2043
1988
2044
updateSeparatorProps ( newProps : Object ) {
1989
2045
this . setState ( state => ( {
1990
- separatorProps : { ...state . separatorProps , ...newProps } ,
2046
+ ...state ,
2047
+ ...newProps ,
1991
2048
} ) ) ;
1992
2049
}
1993
2050
@@ -2060,7 +2117,7 @@ class CellRenderer extends React.Component<
2060
2117
// NOTE: that when this is a sticky header, `onLayout` will get automatically extracted and
2061
2118
// called explicitly by `ScrollViewStickyHeader`.
2062
2119
const itemSeparator = ItemSeparatorComponent && (
2063
- < ItemSeparatorComponent { ...this . state . separatorProps } />
2120
+ < ItemSeparatorComponent { ...this . state } />
2064
2121
) ;
2065
2122
const cellStyle = inversionStyle
2066
2123
? horizontal
0 commit comments