@@ -20,38 +20,38 @@ import type {ViewToken} from 'ViewabilityHelper';
20
20
import type { Props as VirtualizedListProps } from 'VirtualizedList' ;
21
21
22
22
type Item = any ;
23
- type SectionItem = any ;
24
23
25
- type SectionBase = {
26
- // Must be provided directly on each section.
27
- data : $ReadOnlyArray < SectionItem > ,
24
+ export type SectionBase < SectionItemT > = {
25
+ /**
26
+ * The data for rendering items in this section.
27
+ */
28
+ data : $ReadOnlyArray < SectionItemT > ,
29
+ /**
30
+ * Optional key to keep track of section re-ordering. If you don't plan on re-ordering sections,
31
+ * the array index will be used by default.
32
+ */
28
33
key ?: string ,
29
34
30
35
// Optional props will override list-wide props just for this section.
31
- renderItem ?: ?( {
32
- item : SectionItem ,
36
+ renderItem ?: ?( info : {
37
+ item : SectionItemT ,
33
38
index : number ,
34
- section : SectionBase ,
39
+ section : SectionBase < SectionItemT > ,
35
40
separators : {
36
41
highlight : ( ) => void ,
37
42
unhighlight : ( ) => void ,
38
43
updateProps : ( select : 'leading' | 'trailing' , newProps : Object ) => void ,
39
44
} ,
40
45
} ) => ?React . Element < any > ,
41
46
ItemSeparatorComponent ?: ?React . ComponentType < any > ,
42
- keyExtractor ?: ( item : SectionItem , index : ?number ) => string ,
43
-
44
- // TODO: support more optional/override props
45
- // FooterComponent?: ?ReactClass<any>,
46
- // HeaderComponent?: ?ReactClass<any>,
47
- // onViewableItemsChanged?: ({viewableItems: Array<ViewToken>, changed: Array<ViewToken>}) => void,
47
+ keyExtractor ?: ( item : SectionItemT , index ?: ?number ) => string ,
48
48
} ;
49
49
50
- type RequiredProps < SectionT : SectionBase > = {
50
+ type RequiredProps < SectionT : SectionBase < any > > = {
51
51
sections : $ReadOnlyArray < SectionT > ,
52
52
} ;
53
53
54
- type OptionalProps < SectionT : SectionBase > = {
54
+ type OptionalProps < SectionT : SectionBase < any > > = {
55
55
/**
56
56
* Rendered after the last item in the last section.
57
57
*/
@@ -131,10 +131,9 @@ type State = {childProps: VirtualizedListProps};
131
131
* hood. The only operation that might not scale well is concatting the data arrays of all the
132
132
* sections when new props are received, which should be plenty fast for up to ~10,000 items.
133
133
*/
134
- class VirtualizedSectionList < SectionT : SectionBase > extends React.PureComponent<
135
- Props < SectionT > ,
136
- State,
137
- > {
134
+ class VirtualizedSectionList <
135
+ SectionT : SectionBase < any > ,
136
+ > extends React . PureComponent < Props < SectionT > , State > {
138
137
static defaultProps : DefaultProps = {
139
138
...VirtualizedList . defaultProps ,
140
139
data : [ ] ,
@@ -147,8 +146,8 @@ class VirtualizedSectionList<SectionT: SectionBase> extends React.PureComponent<
147
146
viewPosition ?: number ,
148
147
} ) {
149
148
let index = Platform . OS === 'ios' ? params . itemIndex : params . itemIndex + 1 ;
150
- for ( let ii = 0 ; ii < params . sectionIndex ; ii ++ ) {
151
- index += this . props . sections [ ii ] . data . length + 2 ;
149
+ for ( let i = 0 ; i < params . sectionIndex ; i ++ ) {
150
+ index += this . props . getItemCount ( this . props . sections [ i ] . data ) + 2 ;
152
151
}
153
152
const toIndexParams = {
154
153
...params ,
@@ -173,10 +172,12 @@ class VirtualizedSectionList<SectionT: SectionBase> extends React.PureComponent<
173
172
_computeState ( props : Props < SectionT > ) : State {
174
173
const offset = props . ListHeaderComponent ? 1 : 0 ;
175
174
const stickyHeaderIndices = [ ] ;
176
- const itemCount = props . sections . reduce ( ( v , section ) => {
177
- stickyHeaderIndices . push ( v + offset ) ;
178
- return v + section . data . length + 2 ; // Add two for the section header and footer.
179
- } , 0 ) ;
175
+ const itemCount = props . sections
176
+ ? props . sections . reduce ( ( v , section ) => {
177
+ stickyHeaderIndices . push ( v + offset ) ;
178
+ return v + props . getItemCount ( section . data ) + 2 ; // Add two for the section header and footer.
179
+ } , 0 )
180
+ : 0 ;
180
181
181
182
return {
182
183
childProps : {
@@ -185,7 +186,8 @@ class VirtualizedSectionList<SectionT: SectionBase> extends React.PureComponent<
185
186
ItemSeparatorComponent : undefined , // Rendered with renderItem
186
187
data : props . sections ,
187
188
getItemCount : ( ) => itemCount ,
188
- getItem,
189
+ // $FlowFixMe
190
+ getItem : ( sections , index ) => getItem ( props , sections , index ) ,
189
191
keyExtractor : this . _keyExtractor ,
190
192
onViewableItemsChanged : props . onViewableItemsChanged
191
193
? this . _onViewableItemsChanged
@@ -221,42 +223,41 @@ class VirtualizedSectionList<SectionT: SectionBase> extends React.PureComponent<
221
223
trailingSection ?: ?SectionT ,
222
224
} {
223
225
let itemIndex = index ;
224
- const { sections } = this.props;
225
- for (let ii = 0; ii < sections . length ; ii ++ ) {
226
- const section = sections [ ii ] ;
227
- const key = section . key || String ( ii ) ;
226
+ const { getItem , getItemCount , keyExtractor , sections } = this . props ;
227
+ for ( let i = 0 ; i < sections . length ; i ++ ) {
228
+ const section = sections [ i ] ;
229
+ const sectionData = section . data ;
230
+ const key = section . key || String ( i ) ;
228
231
itemIndex -= 1 ; // The section adds an item for the header
229
- if ( itemIndex >= section . data . length + 1 ) {
230
- itemIndex -= section . data . length + 1 ; // The section adds an item for the footer.
232
+ if ( itemIndex >= getItemCount ( sectionData ) + 1 ) {
233
+ itemIndex -= getItemCount ( sectionData ) + 1 ; // The section adds an item for the footer.
231
234
} else if ( itemIndex === - 1 ) {
232
235
return {
233
236
section ,
234
237
key : key + ':header' ,
235
238
index : null ,
236
239
header : true ,
237
- trailingSection : sections [ ii + 1 ] ,
240
+ trailingSection : sections [ i + 1 ] ,
238
241
} ;
239
- } else if ( itemIndex = = = section . data . length ) {
242
+ } else if ( itemIndex === getItemCount ( sectionData ) ) {
240
243
return {
241
244
section ,
242
245
key : key + ':footer' ,
243
246
index : null ,
244
247
header : false ,
245
- trailingSection : sections [ ii + 1 ] ,
248
+ trailingSection : sections [ i + 1 ] ,
246
249
} ;
247
250
} else {
248
- const keyExtractor = section . keyExtractor || this . props . keyExtractor ;
251
+ const extractor = section . keyExtractor || keyExtractor ;
249
252
return {
250
253
section,
251
- key : key + ':' + keyExtractor ( section . data [ itemIndex ] , itemIndex ) ,
254
+ key :
255
+ key + ':' + extractor ( getItem ( sectionData , itemIndex ) , itemIndex ) ,
252
256
index : itemIndex ,
253
- leadingItem : section . data [ itemIndex - 1 ] ,
254
- leadingSection : sections [ ii - 1 ] ,
255
- trailingItem :
256
- section . data . length > itemIndex + 1
257
- ? section . data [ itemIndex + 1 ]
258
- : undefined ,
259
- trailingSection : sections [ ii + 1 ] ,
257
+ leadingItem : getItem ( sectionData , itemIndex - 1 ) ,
258
+ leadingSection : sections [ i - 1 ] ,
259
+ trailingItem : getItem ( sectionData , itemIndex + 1 ) ,
260
+ trailingSection : sections [ i + 1 ] ,
260
261
} ;
261
262
}
262
263
}
@@ -358,7 +359,8 @@ class VirtualizedSectionList<SectionT: SectionBase> extends React.PureComponent<
358
359
info . section . ItemSeparatorComponent || this . props . ItemSeparatorComponent ;
359
360
const { SectionSeparatorComponent} = this . props ;
360
361
const isLastItemInList = index === this . state . childProps . getItemCount ( ) - 1 ;
361
- const isLastItemInSection = info . index === info . section . data . length - 1 ;
362
+ const isLastItemInSection =
363
+ info . index === this . props . getItemCount ( info . section . data ) - 1 ;
362
364
if ( SectionSeparatorComponent && isLastItemInSection ) {
363
365
return SectionSeparatorComponent ;
364
366
}
@@ -523,22 +525,29 @@ class ItemWithSeparator extends React.Component<
523
525
}
524
526
}
525
527
526
- function getItem ( sections : ?$ReadOnlyArray < Item > , index: number): ?Item {
528
+ function getItem (
529
+ props : Props < SectionBase < any >> ,
530
+ sections : ?$ReadOnlyArray < Item > ,
531
+ index : number ,
532
+ ) : ?Item {
527
533
if ( ! sections ) {
528
534
return null ;
529
535
}
530
536
let itemIdx = index - 1 ;
531
- for (let ii = 0; ii < sections . length ; ii ++ ) {
532
- if ( itemIdx === - 1 || itemIdx === sections [ ii ] . data . length ) {
537
+ for ( let i = 0 ; i < sections . length ; i ++ ) {
538
+ const section = sections [ i ] ;
539
+ const sectionData = section . data ;
540
+ const itemCount = props . getItemCount ( sectionData ) ;
541
+ if ( itemIdx === - 1 || itemIdx === itemCount ) {
533
542
// We intend for there to be overflow by one on both ends of the list.
534
543
// This will be for headers and footers. When returning a header or footer
535
544
// item the section itself is the item.
536
- return sections [ ii ] ;
537
- } else if ( itemIdx < sections [ ii ] . data . length ) {
545
+ return section ;
546
+ } else if ( itemIdx < itemCount ) {
538
547
// If we are in the bounds of the list's data then return the item.
539
- return sections [ ii ] . data [ itemIdx ] ;
548
+ return props . getItem ( sectionData , itemIdx ) ;
540
549
} else {
541
- itemIdx -= sections [ ii ] . data . length + 2 ; // Add two for the header and footer
550
+ itemIdx -= itemCount + 2 ; // Add two for the header and footer
542
551
}
543
552
}
544
553
return null ;
0 commit comments