我使用反应虚拟化构建无限装载网格,它也使用AutoSizer
来处理动态网格宽度和WindowScroller
以使得能够与视口滚动触发loadMoreRows。但是,我无法在我的无限加载Grid
的适当位置成功触发loadMoreRows
。无限装载网格是在错误的时间
我已经缩小了两两件事:
当我设置
columnCount
1而不是3下面的示例所示,装载过程似乎按预期方式工作(即您滚动接近底部,它会加载另一批次,直到我们完全加载上一批为止,它才会再次加载)。累积了大约40行(在演示顶部显示)之后,除非向上滚动到上一行,否则无法触发
loadMoreRows()
。要重现此行为,请向下滚动,直到网格停止在网格底部加载新项目。然后尝试向上滚动,然后再次向下滚动,以查看它如何在网格内的某个位置触发loadMoreRows()
。
我把一个最小的plunker演示这将使随机的“Lorem存有”文本片段的3列网格。这个示例内容没有终点,它只会像滚动一样加载。
Plunker演示:http://plnkr.co/edit/uoRdanlB1rXsgBe2Ej8i
import React, { Component } from 'react';
import { render } from 'react-dom';
import { AutoSizer, CellMeasurer, CellMeasurerCache, Grid, InfiniteLoader, WindowScroller } from 'react-virtualized';
const MIN_BATCH_SIZE = 40;
// Return random snippet of lorem ipsum text
const randText =() => {
const text = [
'Lorem ipsum dolor sit amet.',
'Consectetur adipisicing elit.',
'Lorem ipsum dolor sit amet, consectetur adipisicing elit.',
'Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
'Ut enim ad minim veniam.',
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.',
'Excepteur sint occaecat cupidatat non proident.',
'Sunt in culpa qui officia deserunt mollit anim id est laborum.',
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.'
];
return text[Math.floor(Math.random() * text.length)];
};
// Cell data
const list = [];
// -----------------------------------------------------------------------------
// Infinite loading Grid that is AutoSize'd and WindowScroll'd with dynamic cell heights
class App extends Component {
constructor(props) {
super(props);
this.state = {
columnWidth: 300,
columnCount: 3,
rowCount: 0,
isLoading: false
};
this._cache = new CellMeasurerCache({
fixedWidth: true,
defaultHeight: 30
});
this._cellRenderer = this._cellRenderer.bind(this);
this._isRowLoaded = this._isRowLoaded.bind(this);
this._loadMoreRows = this._loadMoreRows.bind(this);
this._onResize = this._onResize.bind(this);
this._onSectionRendered = this._onSectionRendered.bind(this);
}
componentDidMount() {
this.setState({ rowCount: 1 });
}
componentWillUpdate(nextProps, nextState) {
const { columnCount, rowCount } = this.state;
if (rowCount !== nextState.rowCount) {
if (nextState.rowCount > rowCount) {
// Re-measure the row at the index which was last occupied by "loading" content
for (let i = 0; i < columnCount; i++) {
this._cache.clear(this._lastLoadingIndex, i);
}
}
}
}
render() {
const { columnCount, columnWidth, rowCount } = this.state;
return (
<div className="container-fluid">
<h1 className="page-header lead">RV Infinite Grid</h1>
<InfiniteLoader
isRowLoaded={this._isRowLoaded}
loadMoreRows={this._loadMoreRows}
rowCount={rowCount}
threshold={5}
>
{({ onRowsRendered, registerChild }) => {
this._onRowsRendered = onRowsRendered;
return (
<WindowScroller>
{({ height, scrollTop }) => (
<AutoSizer
disableHeight
onResize={this._onResize}
>
{({ width }) => (
<Grid
autoHeight
width={width}
height={height}
scrollTop={scrollTop}
ref={grid => {
this._grid = grid;
registerChild(grid);
}}
columnWidth={columnWidth}
columnCount={columnCount}
rowCount={rowCount}
rowHeight={this._cache.rowHeight}
cellRenderer={this._cellRenderer}
onSectionRendered={this._onSectionRendered}
/>
)}
</AutoSizer>
)}
</WindowScroller>
);
}}
</InfiniteLoader>
</div>
);
}
_isRowLoaded({ index }) {
const { rowCount } = this.state;
return index < rowCount - 1;
}
_loadMoreRows({ startIndex, stopIndex }) {
const { isLoading } = this.state;
const delay = 100 + Math.floor(Math.random() * 3000); // random delay to simulate server response time
if (!isLoading) {
this.setState({
isLoading: true
});
setTimeout(() => {
// Generate some new rows (for this example, we have no actual end point)
for (let i = 0; i < MIN_BATCH_SIZE; i++) {
list.push([ randText(), randText(), randText() ]);
}
// Cancel the "loading" state and update the`rowCount`
this.setState({
isLoading: false,
rowCount: list.length + 1
}, done);
}, delay);
let done;
return new Promise(resolve => done = resolve);
}
}
_cellRenderer({ key, rowIndex, columnIndex, parent, style }) {
const { columnCount, columnWidth, rowCount } = this.state;
let content;
// Render cell content
if (rowIndex < rowCount - 1) {
const cellStyle = Object.assign({}, style, {
backgroundColor: (rowIndex % 2 ? null : '#eee')
});
content = (
<div style={cellStyle}>
<div style={{ padding: '20px' }}>
{list[rowIndex][columnIndex] || <em className="text-muted">empty</em>}
</div>
</div>
);
}
// Render "loading" content
else if (columnIndex === 0) {
// Remember this `index` so we can clear its measurements from the cache later
this._lastLoadingIndex = rowIndex;
const cellStyle = Object.assign({}, style, {
width: (columnWidth * columnCount), // Give loader the full grid width
textAlign: 'center'
});
content = <div style={cellStyle}>Loading...</div>;
}
// Render empty cell (for incomplete rows)
else {
content = <div style={style} />;
}
return (
<CellMeasurer
key={key}
cache={this._cache}
parent={parent}
columnIndex={columnIndex}
rowIndex={rowIndex}
>
{content}
</CellMeasurer>
);
}
_onResize({ width }) {
this.setState({
// Subtracting 30 from `width` to accommodate the padding from the Bootstrap container
columnWidth: (width - 30)/3
});
this._cache.clearAll();
this._grid.recomputeGridSize();
}
_onSectionRendered({ columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex }) {
const { columnCount } = this.state;
const startIndex = rowStartIndex * columnCount + columnStartIndex;
const stopIndex = rowStopIndex * columnCount + columnStopIndex;
this._onRowsRendered({
startIndex,
stopIndex
});
}
}
render(<App />, document.getElementById('root'));
这行在你的'_loadMoreRows'函数中是错误的:'rowCount:Math.ceil(list.length/columnCount)+ 1'。它不符合组件其余部分的逻辑。它应该是:'rowCount:list.length + 1'。这里似乎还有其他的事情发生,但我还不确定它是什么。 – brianvaughn
非常感谢@brianvaughn发现这个错误。我忘了更新,因为我正在调整我的真实应用程序到这个最小的例子。我更新了我的plunker演示,并修复了该行。确实还有一个问题。我会继续探索并尝试。谢谢:) – wavematt
@brianvaughn我能回答我自己的问题。我向我的'onRowsRendered'函数提供了错误的行范围。 – wavematt