0

I have the following example implementing the InfiniteLoader with a Table which is setting the Table rowCount to a known large number (count of logs in the db) and the InfiniteLoader rowCount to the size of the batch of logs I fetch. I need this so that the user knows how much data there is based on the scroll heigth. Otherwise, he would have to scroll to the end and see if more logs are loaded. Could be that I'm misusing the two rowCount props, but whenever I scroll fast to an index close to the end, where data is not loaded yet, data is undefined in the getRowClassName function. I assumed the loadMoreRows would get called in this case.

import React = require('react');
import _ = require('lodash');
import Immutable = require('immutable');
import Api = require('./Api');

const STATUS_LOADING = 1,
      STATUS_LOADED = 2,
      LOG_LIMIT = 200;

interface Props {
    logEntries: Immutable.List<Immutable.Map<string, any>>;
}

interface State {
    logEntries?: Immutable.List<Immutable.Map<string, any>>;
    count?: number;
    loadedRowsMap?: any;
}

class LogViewer extends React.Component<Props, State> {
    constructor(props: Props) {
        super(props);

        this.state = {
            logEntries: props.logEntries,
            count: 0,
            loadedRowsMap: {}
        };
    }

    render() {
        return {this.renderLoader()};
    }

    private renderLoader() {
        const {logEntries, count} = this.state;
        return (
            <InfiniteLoader isRowLoaded={this.isRowLoaded.bind(this)}
                            loadMoreRows={this.loadMoreRows.bind(this)}
                            minimumBatchSize={LOG_LIMIT}
                            rowCount={logEntries.size} >
                {
                    ({onRowsRendered, registerChild}) => (
                        <AutoSizer disableHeight>
                            {
                                ({width}) => (
                                    <Table headerHeight={20}
                                           height={400}
                                           onRowsRendered={onRowsRendered}
                                           ref={registerChild}
                                           rowCount={count}
                                           className='log-entries'
                                           gridClassName='grid'
                                           rowClassName={this.getRowClassName.bind(this)}
                                           headerStyle={{ fontSize: 15 }}
                                           rowGetter={({index}) => logEntries.get(index)}
                                           rowHeight={50}
                                           width={width} >
                                        <Column label='Name'
                                                key='name'
                                                dataKey='name'
                                                width={200} />
                                    </Table>
                                )
                            }
                        </AutoSizer>
                    )
                }
            </InfiniteLoader>
        );
    }

    private getRowClassName({index}) {
        const {logEntries} = this.state;
        if(index > -1) {
            const data = logEntries.get(index);
            return `log-entry ${data.get('name').toLowerCase()}`;
        }

        return '';
    }

    private isRowLoaded({index}) {
        const {loadedRowsMap} = this.state;
        return !!loadedRowsMap[index];
    }

    private loadMoreRows({startIndex, stopIndex}) {
        const {loadedRowsMap, level, logEntries} = this.state;

        _.range(startIndex, stopIndex).forEach(i => {
            loadedRowsMap[i] = STATUS_LOADING;
        });
        this.setState({ loadedRowsMap });

        const offset = Math.floor((startIndex + 1) / LOG_LIMIT);
        return Api.logs(LOG_LIMIT, offset)
            .then(({body: [count, logs]}) => {
                _.range(startIndex, stopIndex).forEach(i => {
                    loadedRowsMap[i] = STATUS_LOADED;
                });
                const newLogs = logEntries.toJS().concat(logs);
                this.setState({
                    count,
                    logEntries: Immutable.fromJS(newLogs)
                });
            });
    }
};
XeniaSis
  • 2,192
  • 5
  • 24
  • 39

1 Answers1

0

Could be that I'm misusing the two rowCount props

You should pass the same rowCount value to both InfiniteLoader and Table. It should either be the total size of all of your data on the server (as shown here) or the size of your local data +1 to allow loading more when a user scrolls near the end (as shown here).

whenever I scroll fast to an index close to the end, where data is not loaded yet, data is undefined in the getRowClassName function. I assumed the loadMoreRows would get called in this case.

loadMoreRows does get called- but it's async. react-virtualized won't block user scrolling until data loads. Your getRowClassName function needs to handle the fact that a user may scroll faster than your lazy-loaded data is able to load. You can show a different "load in progress" UI for unloaded rows if you'd like.

bvaughn
  • 13,300
  • 45
  • 46
  • So, the way I'm using the 2 `rowCount`s is correct? – XeniaSis Feb 14 '17 at 08:14
  • Sorry, I missed that part of your question. Edited my answer to include some more detail. – bvaughn Feb 14 '17 at 16:39
  • On the first link you provided with the total count, it doesn't actually use the same count. The `InfiniteLoader` uses an undefined `remoteRowCount` while the `Table` uses the `list.length` – XeniaSis Feb 15 '17 at 08:00
  • Also, when you said that I can show a loading UI, you mean override the default `rowRenderer` function? – XeniaSis Feb 15 '17 at 09:33
  • That's a typo. They should both use `remoteRowCount` in that example. (I'll update the docs.) Loading UI just means... display something differently if the data hasn't yet loaded, as is shown in the 2nd example. – bvaughn Feb 15 '17 at 16:47
  • Thanks for the clarification! – XeniaSis Feb 16 '17 at 08:42