Reactjs 无限加载网格在错误的时间触发loadMoreRows

Reactjs 无限加载网格在错误的时间触发loadMoreRows,reactjs,react-virtualized,Reactjs,React Virtualized,我正在使用react virtualized构建一个无限加载网格,它还使用AutoSizer处理动态网格宽度,并使用WindowScroller启用视口滚动。但是,我无法在无限加载网格中的适当点成功触发loadMoreRows 我已经缩小了两个范围: 在下面的示例中,当我将columnCount设置为1而不是3时,加载过程似乎按预期工作(即,您滚动到底部附近,它加载另一批,直到我们完全加载上一批,它才会再次加载) 在累积了大约40行(如我的演示顶部所示)之后,除非向上滚动到前面的一行,否则无法

我正在使用react virtualized构建一个无限加载网格,它还使用
AutoSizer
处理动态网格宽度,并使用
WindowScroller
启用视口滚动。但是,我无法在无限加载
网格中的适当点成功触发
loadMoreRows

我已经缩小了两个范围:

  • 在下面的示例中,当我将
    columnCount
    设置为1而不是3时,加载过程似乎按预期工作(即,您滚动到底部附近,它加载另一批,直到我们完全加载上一批,它才会再次加载)

  • 在累积了大约40行(如我的演示顶部所示)之后,除非向上滚动到前面的一行,否则无法触发
    loadMoreRows()
    。要复制此行为,请向下滚动,直到网格停止在网格底部加载新项。然后尝试向上滚动,然后再次向下滚动,查看它如何触发网格中较早的某个位置的
    loadMoreRows()

我制作了一个最小的plunker演示,它将呈现一个3列的随机“lorem ipsum”文本片段网格。本示例内容没有终点,它只是加载您将滚动的内容

Plunker演示:

import React,{Component}来自'React';
从'react dom'导入{render};
从“react virtualized”导入{AutoSizer、CellMeasurer、CellMeasureCache、Grid、InfiniteLoader、WindowScroller};
常数最小批量大小=40;
//返回lorem ipsum文本的随机片段
常量randText=()=>{
常量文本=[
“Lorem ipsum dolor sit amet”,
“圣职精英”,
“Lorem ipsum dolor sit amet,奉献精英。”,
“这是一个临时的劳动和就业机会。”,
“这是我的小意思。”,
“但是,我们必须尽可能少地进行实验,因为我们的实验室必须符合我们的要求。”,
“两人或两人在一起,因为他们在一起的时候都是在一起的。”,
“圣奥卡塔不意外除外。”,
“这是我的错,因为我的动物是我的劳动。”,
“Lorem ipsum door sit amet,adipiscing elit,sed do eiusmod temporal incident ut laboure and dolore magna aliqua.但是,在最小限度的情况下,我们必须在公共消费品上使用实验室。”,
“两人或两人都有权在法庭上受到谴责,这是一种不公平的行为。除了偶尔因不谨慎而被起诉外,还必须为动物和劳力承担罪责。知识本身是一种美德,是一种奉献,是一种精英,是一种暂时性的劳动和巨大的财富。但是,我不知道这一点从最低限度上来说,我们必须在实验室里进行实习,并在日常工作中取得成功。”
];
返回文本[Math.floor(Math.random()*text.length)];
};
//细胞数据
常量列表=[];
// -----------------------------------------------------------------------------
//无限加载网格,可自动调整大小并使用动态单元格高度进行窗口滚动
类应用程序扩展组件{
建造师(道具){
超级(道具);
此.state={
列宽:300,
列数:3,
行数:0,
孤岛加载:false
};
此.\u缓存=新的CellMeasureCache({
固定宽度:对,
默认高度:30
});
this.\u cellRenderer=this.\u cellRenderer.bind(this);
this.\u isrowloated=this.\u isrowloated.bind(this);
this.\u loadMoreRows=this.\u loadMoreRows.bind(this);
this.\u onResize=this.\u onResize.bind(this);
this.\u onSectionRendered=this.\u onSectionRendered.bind(this);
}
componentDidMount(){
this.setState({rowCount:1});
}
组件将更新(下一步,下一步状态){
const{columnCount,rowCount}=this.state;
if(rowCount!==nextState.rowCount){
如果(nextState.rowCount>rowCount){
//重新测量上次被“加载”内容占用的索引行
for(设i=0;i{
这._onRowsRendered=onRowsRendered;
返回(
{({height,scrollTop})=>(
{({width})=>(
{
这个._grid=grid;
注册儿童(grid);
}}
columnWidth={columnWidth}
columnCount={columnCount}
rowCount={rowCount}
rowHeight={this.\u cache.rowHeight}
cellRenderer={this.\u cellRenderer}
onSectionRendered={this.\u onSectionRendered}
/>
)}
)}
);
}}
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'));
_onSectionRendered ({ columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex }) {
  const startIndex = rowStartIndex * columnCount + columnStartIndex
  const stopIndex = rowStopIndex * columnCount + columnStopIndex

  this._onRowsRendered({
    startIndex,
    stopIndex
  })
}
onSectionRendered({ rowStartIndex, rowStopIndex }) {
  this._onRowsRendered({
    startIndex: rowStartIndex,
    stopIndex: rowStopIndex
  })
}