3

I'm trying to create quite a big grid (around 40 col, 1k+ rows - dynamic width) With a sticky header and infinite loader to append more rows. I've used it like in the examples, but when getting more rows performance falls drastically - probably because of dynamic width and cellmeasurer having to check everything. Below is the code for the component I use for printing the table:

import * as React from "react";
import { Grid, CellMeasurer, AutoSizer, ScrollSync, CellMeasurerCache, InfiniteLoader } from 'react-virtualized';


interface VirtualizedTableProps {
    loadMore?: (IndexRange: any) => Promise<any>
    data: any,
    rowHeight?: number,
    headerHeight?: number,
    headers: string[],
    cellClassName?: string,
    headerCellClassName?: string,
    isFullHeight?: boolean,
    getCellContent: Function
}

export class VirtualizedTable extends React.Component<VirtualizedTableProps> {
    static defaultProps = {
        rowHeight: 37,
        headerHeight: 38,
        headerCellClassName: 'Virtualized-table-header',
        cellClassName: 'Virtualized-table-cell',
        isFullHeight: false,
        loadMore: () => {},
    }

    cellMeasurerCache = new CellMeasurerCache({
        fixedHeight: true,
        minWidth: 40,
        //defaultWidth: 40
    });

    componentDidUpdate(prevProps: VirtualizedTableProps) {
        if(prevProps && this.props && prevProps.data !== this.props.data) {
            this.cellMeasurerCache.clearAll();
        }
    }

    render() {
        const {
            rowHeight,
            headerHeight,
            headers,
            cellClassName,
            headerCellClassName, 
            data,
            isFullHeight,
            loadMore,
            getCellContent
        } = this.props;

        const tableHeight = data.length < 9 ? rowHeight * data.length : rowHeight * 10;
        return (
                <div style={{ height: '100%' }}>
                    <ScrollSync>
                        {({ onScroll, scrollLeft }) => {
                            return (
                                <AutoSizer onResize={() => { this.cellMeasurerCache.clearAll() }}>
                                    {({ width, height }) => {
                                        return (
                                            <div>
                                                    <Grid
                                                        className={'Virtualized-table-header-wrapper'}
                                                        columnWidth={this.cellMeasurerCache.columnWidth}
                                                        columnCount={headers.length}
                                                        deferredMeasurementCache={this.cellMeasurerCache}
                                                        height={35}
                                                        overscanColumnCount={headers.length}
                                                        rowHeight={headerHeight}
                                                        rowCount={1}
                                                        scrollLeft={scrollLeft}
                                                        width={width - 17} // needs to be shorter because of the system scroll
                                                        cellRenderer={({ columnIndex, key, parent, rowIndex, style }) => {
                                                            const value = headers[columnIndex];
                                                            return (
                                                                <CellMeasurer
                                                                    cache={this.cellMeasurerCache}
                                                                    columnIndex={columnIndex}
                                                                    key={key}
                                                                    parent={parent}
                                                                    rowIndex={rowIndex}
                                                                >
                                                                    <div className={headerCellClassName} style={{
                                                                        ...style,
                                                                    }}>
                                                                        <span style={{ padding: '0 20px', whiteSpace: 'nowrap' }}>{value}</span>
                                                                    </div>
                                                                </CellMeasurer>
                                                            );
                                                        }}
                                                    />

                                                <InfiniteLoader
                                                isRowLoaded={({ index }) => !!data[index]}
                                                loadMoreRows={loadMore}
                                                rowCount={100000000000} // this needs to be like this
                                                >
                                                    {({ onRowsRendered, registerChild }) => {
                                                        return (
                                                            <Grid
                                                                onSectionRendered={({ rowStartIndex, rowStopIndex }) => {
                                                                    const startIndex = rowStartIndex;
                                                                    const stopIndex = rowStopIndex;
                                                                    onRowsRendered({ startIndex, stopIndex })
                                                                }}
                                                                ref={registerChild}
                                                                columnCount={headers.length}
                                                                columnWidth={this.cellMeasurerCache.columnWidth}
                                                                deferredMeasurementCache={this.cellMeasurerCache}
                                                                height={isFullHeight ? height - headerHeight : tableHeight }
                                                                overscanColumnCount={headers.length}
                                                                overscanRowCount={50}
                                                                rowHeight={rowHeight}
                                                                width={width}
                                                                rowCount={data.length}
                                                                onScroll={onScroll}
                                                                cellRenderer={({ columnIndex, key, parent, rowIndex, style }) => {
                                                                    const conversation = data[rowIndex];
                                                                    return (
                                                                        <CellMeasurer
                                                                        cache={this.cellMeasurerCache}
                                                                        columnIndex={columnIndex}
                                                                        key={key}
                                                                        parent={parent}
                                                                        rowIndex={rowIndex+1}
                                                                        >
                                                                            <div className={`${cellClassName} ${rowIndex % 2 ? 'even' : ''}`} style={{
                                                                                ...style,
                                                                                whiteSpace: 'nowrap',
                                                                            }}>
                                                                                <span style={{ padding: '0 20px' }}>{getCellContent(conversation, columnIndex)}</span>
                                                                            </div>
                                                                        </CellMeasurer>
                                                                    );
                                                                }}
                                                            />
                                                        )
                                                    }}
                                                </InfiniteLoader>
                                            </div>
                                        )
                                    }}
                                </AutoSizer>
                            )
                        }}
                    </ScrollSync>
                </div>
        )
    }
}

I'm not sure if I use the cellMeasurer as expected, which may cause the performance drop. Problem is with the sticky header getting out of line when scrolling horizontaly with bigger amount of rows (Not the slow performance of onscroll, it just calculates sticky grids dimensions incorrect):

enter image description here

Maybe I use the cellMeasurer incorrectly? Or some props are set wrong? Anyone had similar problems?

mjanisz1
  • 1,478
  • 3
  • 17
  • 43

0 Answers0