Извините, если заголовок несколько вводит в заблуждение. Я использую InfiniteLoader
с Table
, и проблема в том, что почти всегда общее количество данных, которые я хочу загрузить, огромно. И если бы я должен был добавлять данные каждый раз, когда был вызван loadMoreRows
, я бы в конечном итоге сохранил в состоянии более 100000 записей, что, я думаю, было бы плохо для производительности.всегда загружать данные только для одного диапазона индексов
Мне было интересно, нельзя ли добавлять данные каждый раз. Я пробовал только устанавливать журналы и статус для загрузки только с startIndex
на stopIndex
, но каждый раз, когда я прокручиваю loadMoreRows
, вызывается несколько раз.
Вот то, что я до сих пор, с тем, что я пытался, как упоминалось выше
'use strict';
import React = require('react');
import _ = require('lodash');
import Immutable = require('immutable');
import moment = require('moment-timezone');
import {AutoSizer, InfiniteLoader, Table, Column} from 'react-virtualized';
interface Props {
logEntries: Immutable.List<Immutable.Map<string, any>>;
count: number;
timezone: string;
logLimit: number;
loadedRowsMap: { [index: number]: number; };
onLoadMoreRows: (param: {startIndex: number, stopIndex: number}) => Promise<any>;
}
class LogLoader extends React.Component<Props, {}> {
render() {
const {logEntries, count, logLimit} = this.props;
const headers: {
name: string;
dataKey: string;
width: number;
cellDataGetter?: (param: {rowData: any}) => any;
}[] = [
{ name: 'Time', dataKey: 'dtCreated', width: 95, cellDataGetter: this.renderTime.bind(this) },
{ name: 'Level', dataKey: 'levelname', width: 65 },
{ name: 'Message', dataKey: 'message', width: 70, cellDataGetter: this.renderMessage.bind(this) }
];
return (
<InfiniteLoader isRowLoaded={this.isRowLoaded.bind(this)}
loadMoreRows={this.props.onLoadMoreRows}
minimumBatchSize={logLimit}
rowCount={count} >
{
({onRowsRendered, registerChild}) => (
<AutoSizer disableHeight>
{
({width}) => (
<Table headerHeight={20}
height={400}
onRowsRendered={onRowsRendered}
rowRenderer={this.rowRenderer}
ref={registerChild}
rowCount={count}
className='log-entries'
gridClassName='grid'
headerClassName='header'
rowClassName={this.getRowClassName.bind(this)}
rowGetter={({index}) => logEntries.get(index)}
rowHeight={this.calculateRowHeight.bind(this)}
width={width} >
{
headers.map(({name, dataKey, cellDataGetter, width}) =>
<Column label={name}
key={name}
className={`${name.toLowerCase()} column`}
dataKey={dataKey}
cellDataGetter={cellDataGetter || this.renderTableCell.bind(this)}
width={width} />
)
}
</Table>
)
}
</AutoSizer>
)
}
</InfiniteLoader>
);
}
private calculateRowHeight({index}) {
const rowData = this.props.logEntries.get(index);
if(!rowData) {
return 0;
}
const msg = this.renderMessage({rowData});
const div = document.createElement('div');
const span = document.createElement('span');
span.style.whiteSpace = 'pre';
span.style.wordBreak = 'break-all';
span.style.fontSize = '12px';
span.style.fontFamily = 'monospace';
span.style.display = 'table-cell';
span.innerHTML = msg;
div.appendChild(span);
document.body.appendChild(div);
const height = div.offsetHeight;
document.body.removeChild(div);
return height;
}
private rowRenderer(params: any) {
const {key, className, columns, rowData, style} = params;
if(!rowData) {
return (
<div className={className}
key={key}
style={style} >
Loading...
</div>
);
}
return (
<div className={className}
key={key}
style={style} >
{columns}
</div>
);
}
private renderTableCell({rowData, dataKey}) {
if(!rowData) {
return null;
}
return rowData.get(dataKey);
}
private renderMessage({rowData}) {
if(!rowData) {
return null;
}
return rowData.get('message');
}
private renderTime({rowData}) {
if(!rowData) {
return null;
}
return moment(rowData.get('dtCreated'))
.tz(this.props.timezone)
.format('HH:mm:ss.SSS');
}
private getRowClassName({index}) {
const {logEntries} = this.props;
const data = logEntries.get(index);
if(data) {
return `log-entry ${data.get('levelname').toLowerCase()}`;
}
return '';
}
private isRowLoaded({index}) {
return !!this.props.loadedRowsMap[index];
}
}
export = LogLoader;
и здесь loadMoreRows
передается от родительского компонента
private loadMoreRows({startIndex, stopIndex}) {
const {loadedRowsMap, logEntries} = this.state,
indexRange = _.range(startIndex, stopIndex + 1),
updatedLoadedRowsMap = {};
indexRange.forEach(i => { updatedLoadedRowsMap[i] = STATUS_LOADING; });
this.setState({ loadedRowsMap: updatedLoadedRowsMap, loading: true });
return Api.scriptLogs(null, { id: this.props.id })
.then(({body: [count, logs]}) => {
indexRange.forEach(i => { updatedLoadedRowsMap[i] = STATUS_LOADED; });
const newLogs = logEntries.splice((stopIndex - startIndex + 1), 0, ...logs).toJS();
this.setState({
count,
logEntries: Immutable.fromJS(newLogs),
loadedRowsMap: updatedLoadedRowsMap,
loading: false
});
});
}
Я думаю, вы неправильно поняли мой вопрос. Посмотрим, смогу ли я объяснить это лучше. Каждый раз, когда вызывается 'loadMoreRows', я просто устанавливаю данные из' startIndex' в 'stopIndex'. Например, я храню от 0-199, а затем от 200-399, поэтому 0-199 пуст. Но всякий раз, когда я прокручиваю где-то между 200-399, 'loadMoreRows' вызывается для 0-199. Я бы хотел, чтобы это вызывалось только тогда, когда я собираюсь отображать индексы до или после моих сохраненных данных. Это возможно? – XeniaSis
Это звучит не так. 'InfiniteLoader' только сканирует и запрашивает загружаемые строки, которые в настоящее время отображаются. У вас это работает в любом месте, где я мог видеть это в действии? – brianvaughn
К сожалению нет. Я отредактировал свой «loadMoreRows» до версии, которую я сейчас имею, и журналов, которые я вижу, когда 'console.log'ging' startIndex' и 'stopIndex': 200 399, 0 199, 200 399, 0 199, 400 58727 (моя общая count), 133 332. Честно говоря, я не знаю – XeniaSis