in extensions/virtuallistview/src/VirtualListView.tsx [1108:1271]
render() {
const itemsRendered: JSX.Element[] = [];
this._navigatableItemsRendered = [];
if (this.props.logInfo) {
this.props.logInfo('Rendering ' + this._itemsInRenderBlock + ' Items...');
}
// Build a list of all the cells we're going to render. This includes all of the active
// cells plus any recycled (offscreen) cells.
let cellList: {
cellInfo: VirtualCellInfo<ItemInfo>;
item: ItemInfo | undefined;
itemIndex: number | undefined;
}[] = [];
for (let i = 0; i < this._itemsInRenderBlock; i++) {
const itemIndex = this._itemsAboveRenderBlock + i;
const item = this.props.itemList[itemIndex];
const virtualCellInfo = this._activeCells.get(item.key)!;
assert(virtualCellInfo, 'Active Cell not found for key ' + item.key + ', index=' + i);
cellList.push({
cellInfo: virtualCellInfo,
item: item,
itemIndex: itemIndex,
});
if (item.isNavigable) {
this._navigatableItemsRendered.push({ key: item.key, vc_key: virtualCellInfo.virtualKey });
}
}
for (const virtualCellInfo of this._recycledCells) {
assert(virtualCellInfo, 'Recycled Cells array contains a null/undefined object');
cellList.push({
cellInfo: virtualCellInfo,
item: undefined,
itemIndex: undefined,
});
}
// Sort the list of cells by virtual key so the order doesn't change. Otherwise
// the underlying render engine (the browser or React Native) treat it as a DOM
// change, and perf suffers.
cellList = cellList.sort((a, b) => a.cellInfo.virtualKey < b.cellInfo.virtualKey ? 1 : -1);
let focusIndex: number | undefined;
if (this.state.lastFocusedItemKey === undefined) {
const itemToFocus = _.minBy(cellList, cell => {
if (!cell.item || !cell.item.isNavigable) {
return Number.MAX_VALUE;
}
return cell.itemIndex;
});
if (itemToFocus) {
focusIndex = itemToFocus.itemIndex;
}
}
for (const cell of cellList) {
let tabIndexValue = -1;
let isFocused = false;
let isSelected = false;
if (cell.item) {
if (cell.item.isNavigable) {
if (cell.itemIndex === focusIndex) {
tabIndexValue = 0;
} else {
tabIndexValue = cell.item.key === this.state.lastFocusedItemKey ? 0 : -1;
}
if (cell.item.key === this.state.selectedItemKey) {
isSelected = true;
}
}
if (cell.item.key === this.state.lastFocusedItemKey) {
isFocused = true;
}
}
// We disable transform in Android because it creates problem for screen reader order.
// We update the keys in order to make sure we re-render cells, as once we enable native animation for a view.
// We can't disable it.
itemsRendered.push(
<VirtualListCell
ref={ cell.cellInfo.cellRef }
key={ this._isAndroidScreenReaderEnabled() ? _accessibilityVirtualKeyPrefix +
cell.cellInfo.virtualKey : cell.cellInfo.virtualKey }
onLayout={ !cell.cellInfo.isHeightConstant ? this._onLayoutItem : undefined }
onAnimateStartStop={ this._onAnimateStartStopItem }
itemKey={ cell.item ? cell.item.key : undefined }
item={ cell.item }
left={ 0 }
width={ this._contentWidth }
top={ cell.cellInfo.top }
isVisible={ cell.cellInfo.isVisible }
isActive={ cell.item ? true : false }
isFocused={ isFocused }
isSelected={ isSelected }
tabIndex={ tabIndexValue }
onItemFocused={ this._onItemFocused }
onItemSelected={ this._onItemSelected }
shouldUpdate={ !this.props.skipRenderIfItemUnchanged || cell.cellInfo.shouldUpdate }
showOverflow={ this.props.showOverflow }
isScreenReaderModeEnabled={ this._isAndroidScreenReaderEnabled() }
renderItem={ this.props.renderItem }
onKeyPress={ this._onKeyDown }
/>,
);
cell.cellInfo.shouldUpdate = false;
}
if (this.props.logInfo) {
// [NOTE: For debugging] This shows the order in which virtual cells are laid out.
const domOrder = _.map(cellList, c => {
const itemKey = c.item ? c.item.key : null;
const itemIndex = c.item ? c.itemIndex : null;
return 'vKey: ' + c.cellInfo.virtualKey + ' iKey: ' + itemKey + ' iIdx: ' + itemIndex;
}).join('\n');
this.props.logInfo(domOrder);
this.props.logInfo('Item Render Complete');
}
const scrollViewStyle = [_styles.scrollContainer];
let staticContainerStyle: (RX.Types.ViewStyleRuleSet | RX.Types.AnimatedViewStyleRuleSet)[] = [_styles.staticContainer];
if (this.props.style) {
if (Array.isArray(this.props.style)) {
staticContainerStyle = staticContainerStyle.concat(this.props.style as RX.Types.ViewStyleRuleSet[]);
} else {
staticContainerStyle.push(this.props.style as RX.Types.ViewStyleRuleSet);
}
}
staticContainerStyle.push(this._containerAnimatedStyle);
return (
<RX.ScrollView
ref={ this._scrollViewRef }
testId={ this.props.testId }
onLayout={ this._onLayoutContainer }
onScroll={ this._onScroll }
scrollXAnimatedValue={ this.props.scrollXAnimatedValue }
scrollYAnimatedValue={ this.props.scrollYAnimatedValue }
keyboardDismissMode={ this.props.keyboardDismissMode }
keyboardShouldPersistTaps={ this.props.keyboardShouldPersistTaps }
scrollsToTop={ this.props.scrollsToTop }
scrollEventThrottle={ this.props.scrollEventThrottle || 32 } // 32ms throttle -> ~30 events per second max
style={ scrollViewStyle }
bounces={ !this.props.disableBouncing }
onKeyPress={ this._onKeyDown }
scrollEnabled={ !this.props.disableScrolling }
scrollIndicatorInsets={ this.props.scrollIndicatorInsets }
>
<RX.Animated.View style={ staticContainerStyle }>
{ itemsRendered }
</RX.Animated.View>
</RX.ScrollView>
);
}