render()

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>
        );
    }