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

时间:2017-08-15 21:28:47

标签: reactjs react-virtualized

我使用react-virtualized来构建无限加载网格,它还使用AutoSizer来处理动态网格宽度,使用WindowScroller来启用视口滚动。但是,我无法在无限加载loadMoreRows的适当位置成功触发Grid

我缩小了两件事:

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

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

我整理了一个最小的plunker演示,它将呈现随机“lorem ipsum”文本片段的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'));

1 个答案:

答案 0 :(得分:2)

回答我自己的问题......

事实证明,我的_onSectionRendered函数为我的_onRowsRendered函数提供了错误的范围。我承认我盲目地将InfiniteLoader docs中的示例代码段复制并粘贴到我自己的项目中,直到我对它有了更多的了解。我一开始就按照文档中的方式返回单元格范围是有道理的,直到我提醒自己InfiniteLoader正在查看行,而不是单元格。

文档中的示例将startIndexstopIndex设置为一系列单元格,而不是返回行范围。

_onSectionRendered ({ columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex }) {
  const startIndex = rowStartIndex * columnCount + columnStartIndex
  const stopIndex = rowStopIndex * columnCount + columnStopIndex

  this._onRowsRendered({
    startIndex,
    stopIndex
  })
}

要解决我的问题,我只会将rowStartIndexrowStopIndex传递给onRowsRendered()

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

我的plunker example已更新。