React virtualized 调用loadMoreRows后未定义的索引

React virtualized 调用loadMoreRows后未定义的索引,react-virtualized,React Virtualized,下面的示例使用一个表来实现InfiniteLoader,该表将表的行数设置为已知的大量(数据库中的日志数),并将InfiniteLoader行数设置为我获取的一批日志的大小。我需要这样做,以便用户知道有多少数据是基于滚动高度。否则,他将不得不滚动到末尾,查看是否加载了更多日志。可能是我误用了两个rowCount道具,但是每当我快速滚动到接近末尾的索引时,如果数据尚未加载,data在getRowClassName函数中未定义。我假设在这种情况下会调用loadMoreRows import Reac

下面的示例使用一个
表来实现
InfiniteLoader
,该表将
表的行数设置为已知的大量(数据库中的日志数),并将
InfiniteLoader
行数设置为我获取的一批日志的大小。我需要这样做,以便用户知道有多少数据是基于滚动高度。否则,他将不得不滚动到末尾,查看是否加载了更多日志。可能是我误用了两个
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)
                });
            });
    }
};
import React=require('React');
导入=require('lodash');
import Immutable=require('Immutable');
导入Api=需要('./Api');
const STATUS_LOADING=1,
状态_已加载=2,
对数极限=200;
界面道具{
日志条目:不可变。列表;
}
界面状态{
日志条目?:不可变。列表;
计数?:数字;
loadedRowsMap?:任何;
}
类LogViewer扩展了React.Component{
建造师(道具:道具){
超级(道具);
此.state={
日志条目:props.logEntries,
计数:0,
loadedRowsMap:{}
};
}
render(){
返回{this.renderLoader()};
}
私有renderLoader(){
const{logEntries,count}=this.state;
返回(
{
({onRowsRendered,registerChild})=>(
{
({width})=>(
logEntries.get(index)}
行高={50}
宽度={width}>
)
}
)
}
);
}
私有getRowClassName({index}){
const{logEntries}=this.state;
如果(索引>-1){
const data=logEntries.get(索引);
返回`log entry${data.get('name').toLowerCase()}`;
}
返回“”;
}
私有isRowload({index}){
const{loadedRowsMap}=this.state;
return!!loadedRowsMap[索引];
}
私有loadMoreRows({startIndex,stopIndex}){
const{loadedRowsMap,level,logEntries}=this.state;
_.range(startIndex,stopIndex).forEach(i=>{
loadedRowsMap[i]=加载状态;
});
this.setState({loadedRowsMap});
常数偏移=数学层((起始索引+1)/对数极限);
返回Api.logs(日志限制、偏移量)
。然后({body:[计数,日志]})=>{
_.range(startIndex,stopIndex).forEach(i=>{
loadedRowsMap[i]=状态_已加载;
});
const newLogs=logEntries.toJS().concat(日志);
这是我的国家({
计数
logEntries:Immutable.fromJS(newLogs)
});
});
}
};
可能是我误用了这两个
rowCount
道具

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

每当我快速滚动到接近末尾的索引,在该索引中尚未加载数据时,
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)
                });
            });
    }
};

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

那么,我使用2
rowCount
s的方式是正确的吗?对不起,我错过了您问题的这一部分。编辑了我的答案以包含更多的细节。在您提供的第一个链接中,总计数实际上并不使用相同的计数。
InfiniteLoader
使用未定义的
remoteRowCount
,而
表使用
列表。length
另外,当您说我可以显示加载UI时,您的意思是覆盖默认的
rowRenderer
函数?这是一个输入错误。在该示例中,它们都应该使用
remoteRowCount
。(我会更新文档。)加载UI只是意味着。。。如果数据尚未加载,则显示不同的内容,如第二个示例所示。