【问题标题】:Undefined index after loadMoreRows is called调用 loadMoreRows 后未定义的索引
【发布时间】:2017-02-13 11:31:50
【问题描述】:

我有以下示例使用Table 实现InfiniteLoader,它将Table rowCount 设置为已知的大数字(数据库中的日志计数),并将InfiniteLoader rowCount 设置为我获取的一批日志的大小。我需要这个,以便用户知道基于滚动高度有多少数据。否则,他将不得不滚动到最后,看看是否加载了更多日志。可能是我滥用了两个 rowCount 道具,但是每当我快速滚动到接近末尾的索引时,尚未加载数据时,data 在 getRowClassName 函数中是未定义的。我假设在这种情况下会调用loadMoreRows。

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

【问题讨论】:

    标签: react-virtualized


    【解决方案1】:

    可能是我误用了两个 rowCount 道具

    您应该将相同的rowCount 值传递给InfiniteLoader 和Table。它应该是服务器上所有数据的总大小(如 here 所示)或本地数据的大小 +1 以允许在用户滚动到末尾时加载更多(如 here 所示)。

    每当我快速滚动到接近末尾的索引时,数据尚未加载,getRowClassName 函数中的数据未定义。我假设在这种情况下会调用 loadMoreRows。

    loadMoreRows 确实被调用了——但它是异步的。 react-virtualized 在数据加载之前不会阻止用户滚动。您的getRowClassName 函数需要处理用户滚动速度可能快于延迟加载数据能够加载的事实。如果您愿意,可以为未加载的行显示不同的“正在加载”UI。

    【讨论】:

    • 那么,我使用 2 rowCounts 的方式正确吗?
    • 对不起,我错过了你问题的那一部分。编辑了我的答案以包含更多细节。
    • 在您提供总计数的第一个链接上,它实际上并没有使用相同的计数。 InfiniteLoader 使用未定义的remoteRowCount 而Table 使用list.length
    • 另外,当你说我可以显示加载 UI 时,你的意思是覆盖默认的 rowRenderer 函数?
    • 这是一个错字。在该示例中,他们都应该使用remoteRowCount。 (我将更新文档。)加载 UI 只是意味着......如果数据尚未加载,则显示不同的内容,如第二个示例所示。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-25
    • 2017-04-11
    • 1970-01-01
    • 2012-06-01
    • 1970-01-01
    • 2014-02-15
    相关资源
    最近更新 更多