_isRowLoaded 和 _loadMoreRows 没有被调用反应虚拟化

_isRowLoaded and _loadMoreRows not getting called react virtualized

我的 _loadMoreRows 和 _isRowLoaded 没有被调用,所以 loadedRowsMap 仍然是空的,我无法识别加载的行以避免发出 HTTP 请求。

这是我的完整代码:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import {formatDate} from '../../helper/date';
import { recentActivitiAction } from '../actions/dashboardAction';
import { BeatLoader } from 'react-spinners';
import {AutoSizer, List, CellMeasurer, InfiniteLoader, CellMeasurerCache} from 'react-virtualized';
import styles from '../../css/AutoSizer.module.css';
import Skeleton from 'react-skeleton-loader';

const STATUS_LOADING = 1;
const STATUS_LOADED = 2;

const mapStateToProps = (state) => {
    return {
        recentActList: state.dashboardReducer.recentActList,
        activitiLoading: state.dashboardReducer.activitiLoading
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        getRecentActivites: (postData, callback) => {
            dispatch(recentActivitiAction(postData, callback));
        }
    };
}
class RecentActivitiComp extends Component {
    constructor(props) {
        super(props);
        this.state = {
            loadedRowCount: 0,
            loadedRowsMap: {},
            loadingRowCount: 0,
        };

        this.cache = new CellMeasurerCache({
            fixedWidth: true,
            defaultHeight: 100
        });

        this._timeoutIdMap = {};
        this._isRowLoaded = this._isRowLoaded.bind(this);
        this._loadMoreRows = this._loadMoreRows.bind(this);
        this.renderRow = this.renderRow.bind(this);
        this.onRowsRendered = this.onRowsRendered.bind(this);
        this.noRowsRenderer = this.noRowsRenderer.bind(this);
    }
    componentWillUnmount() {
        Object.keys(this._timeoutIdMap).forEach(timeoutId => {
          clearTimeout(timeoutId);
        });
    }
    componentDidMount() {
        var postData = {
            "userName": "admin",
            "queryType": "GET_RECENT_PROJECTS",
            "data": {
                pageStart: 1,
                pageEnd: 20
            } 
        };
        this.props.getRecentActivites(postData, this.recentActResponse.bind(this));
    }

    updateDimensions() {
        this.cache.clearAll();
        this.activitiList.recomputeRowHeights();
    }

    recentActResponse(response) {
        if (response.status === "FAILED") {
           // handle error
        }
    }
    _fieldGenerator(row, index) {
        var formattedDate = formatDate(row.lastModified),
        output = '', JSX = '';

        if(formattedDate) {
            formattedDate = formattedDate.split('-');
            output = (
                <div className="project-info-value byline">
                    <span>{formattedDate[0]}<sup>{formattedDate[1]}</sup> {formattedDate[2]} {formattedDate[3]}</span> by <a>{row.modifiedBy}</a>   
                </div>
            )
        } else {
            output = (
                <div className="project-info-value byline">
                    <span>Invalid Date by </span> <a>{row.modifiedBy}</a>                                                                                                                                                                               
                </div>
            )
        }

        if(row.action === "upcoming-release") {   
            JSX = 
                <li key={index}>
                    <div className="block">
                        <div className="block_content">
                            <h2 className="title">{row.action}</h2>
                            {output}
                            <p className="excerpt">{row.notes}<a> Read More</a></p>
                        </div>
                    </div>
                </li>
        } else if(row.action === "created") {
            JSX =

                    <li key={index}>
                        <div className="block">
                            <div className="block_content">
                                <h2 className="title">{row.type} <a>{row.name}</a> {row.action}</h2>
                                {output}
                                <p className="excerpt"></p>
                            </div>
                        </div>
                    </li>

        } else if(row.action === "modified") {
            JSX = 
                <li key={index}>
                    <div className="block">
                        <div className="block_content">
                            <h2 className="title">{row.type} <a>{row.name}</a> {row.action}</h2>
                            {output}
                            <p className="excerpt"></p>
                        </div>
                    </div>
                </li>

        } else {
            JSX = 
                <li key={index}>
                    <div className="block">
                        <div className="block_content">
                            <h2 className="title"><a>{row.person}</a> added to <a>{row.addedTo}</a></h2>
                            {output}
                            <p className="excerpt"></p>
                        </div>
                    </div>
                </li>
        }
        return JSX;
    }
    renderRow({ index, key, style, parent }) {
        var JSX = '', content = '';
        const list = this.props.recentActList
        const {loadedRowsMap} = this.state;

        if (loadedRowsMap[index] === STATUS_LOADED) {
            const row = list[index];
            JSX = this._fieldGenerator(row, index);
            content = (
                JSX
            );
        } else {
            content = (
                <div className={styles.placeholder} style={{width: 480}} />
            );
        }
        return (
            <CellMeasurer
              cache={this.cache}
              columnIndex={0}
              key={key}
              parent={parent}
              rowIndex={index}
            >
            {({ measure }) => (
                <div key={key} style={{...style}} onLoad={measure}>
                    {content}
                </div>
            )}
            </CellMeasurer>
        );
    }
    _isRowLoaded({index}) {
        const {loadedRowsMap} = this.state;
        return !!loadedRowsMap[index]; // STATUS_LOADING or STATUS_LOADED
    }
    _loadMoreRows({startIndex, stopIndex}) {
        const {loadedRowsMap, loadingRowCount} = this.state;
        const increment = stopIndex - startIndex + 1;

        for (var i = startIndex; i <= stopIndex; i++) {
            loadedRowsMap[i] = STATUS_LOADING;
        }

        this.setState({
          loadingRowCount: loadingRowCount + increment,
        });

        const timeoutId = setTimeout(() => {
            const {loadedRowCount, loadingRowCount} = this.state;
            delete this._timeoutIdMap[timeoutId];

            for (var i = startIndex; i <= stopIndex; i++) {
                loadedRowsMap[i] = STATUS_LOADED;
            }

            this.setState({
                loadingRowCount: loadingRowCount - increment,
                loadedRowCount: loadedRowCount + increment,
            });
            promiseResolver();
        }, 1000 + Math.round(Math.random() * 2000));

        this._timeoutIdMap[timeoutId] = true;

        let promiseResolver;

        return new Promise(resolve => {
            promiseResolver = resolve;
        });
    }    
    noRowsRenderer() {
        return <div className={styles.noRows}>No rows</div>;
    }
    onRowsRendered({overscanStartIndex,  overscanStopIndex, startIndex, stopIndex}) {
        const list = this.props.recentActList.length ? this.props.recentActList : [];
        const {loadedRowsMap} = this.state;

        console.log(startIndex, stopIndex, this.state.loadedRowCount);
        // if(startIndex + 10 === list.length && this._isRowLoaded(startIndex) !== STATUS_LOADED) {
        //     var postData = {
        //         "userName": "admin",
        //         "queryType": "GET_RECENT_PROJECTS",
        //         "data": {
        //             pageStart: 1,
        //             pageEnd: 10
        //         } 
        //     };
        //     this.props.getRecentActivites(postData, this.recentActResponse.bind(this));
        // } 
    }
    render() {
        const list = this.props.recentActList.length ? this.props.recentActList : [];
        const {loadedRowCount, loadingRowCount} = this.state;

        return ( 
            <div className="recent left_panel">
                <div className="x_panel">
                    <div className="x_title sub_title">
                        <h2>Recent Activites</h2>
                    </div>
                    <div className="x_content">
                        <div className="dashboard-widget-content">
                            <ul className="list-unstyled timeline widget">
                                <InfiniteLoader
                                    isRowLoaded={this._isRowLoaded}
                                    loadMoreRows={this._loadMoreRows}
                                    rowCount={list.length}> 
                                    {({onRowsRendered, registerChild}) => (
                                        <div className={styles.list}>
                                            <AutoSizer onResize={this.updateDimensions.bind(this)}>
                                                {({width, height}) => (
                                                    <List
                                                    ref={(ref) => {
                                                        this.activitiList = ref;
                                                        registerChild(ref);
                                                    }}
                                                    noRowsRenderer={this.noRowsRenderer}
                                                    onRowsRendered={this.onRowsRendered}
                                                    deferredMeasurementCache={this.cache}
                                                    width={width}
                                                    height={height}
                                                    deferredMeasurementCache={this.cache}
                                                    rowHeight={this.cache.rowHeight}
                                                    rowRenderer={this.renderRow}
                                                    rowCount={list.length}  /* Initially render 20 records */
                                                    />
                                                )}
                                            </AutoSizer>
                                        </div>
                                    )}
                                </InfiniteLoader>
                            </ul> 
                            {/* <div className="align-right">
                                <a href="http://karthik.jivox.com/studio/eam/production/index.php#" 
                                    className="btn-jivox-1">
                                    <span className="btn-icon">
                                        <i className="fas fa-chevron-down" aria-hidden="true"></i>
                                    </span> Show More Activities
                                </a>
                            </div> */}
                        </div>
                    </div>
                </div>
            </div>
        );
    }
}
const RecentActiviti = connect(mapStateToProps, mapDispatchToProps)(RecentActivitiComp);
export default RecentActiviti;

如您所见,我在 didMount 阶段进行了 API 调用,因此用数据填充了我的 redux 存储。数据很好。但是 isRowLoaded 和 loadMoreRows 没有被调用。

我调试了 infiniteLoader.example.js 的示例代码,在初始渲染期间调用了这两个函数并正确设置了 loadedRowsMap。

我做错了什么? :-( 任何帮助将不胜感激。

(您发布的代码有很多内容;如果您删除与您的问题无关的部分,您可能会得到更好的答复。)


我的建议是查看您为 list.length 获得的值,您将其传递给 InfiniteLoader 的 rowCount 道具。 InfiniteLoader 只会调用 loadMoreRows 如果 rowCount 高于它具有数据的行数。

例如:在第一次渲染时,值为0,因为你还没有获取任何数据。这可以防止在第一次渲染期间调用 loadMoreRows


我还注意到您将 loadedRowCountloadingRowCount 保持在状态中,但您从未将它们用于任何用途。我认为这与您的问题无关,但也可能不是故意的。

经过一些修改,我已经解决了这个问题。

const mapStateToProps = (state) => {
    return {
        myList: state.dashboardReducer.myList
    }
}

const list = this.props.myList.length ? this.props.recentActList : [];
const rowCount = list.length + (moreToBeLoaded ? 1 : 0);

要知道为什么多加载了 1 行,请查看此 post react-virtualized InfiniteLoader/List - working example using AJAX

所以我的代码中的问题是我正在编写我的自定义 onRowsRendered 函数并且它没有正确处理响应。现在我更改了代码以使用 InfiniteLoader 传递的代码。

希望对您有所帮助。

<InfiniteLoader
        isRowLoaded={this._isRowLoaded}
        loadMoreRows={this._loadMoreRows}
        rowCount={rowCount}>
        {({onRowsRendered, registerChild}) => (
            <div className={styles.list}>
                <AutoSizer onResize={this.updateDimensions.bind(this)}>
                    {({width, height}) => (
                        <ActiviitiList
                        ref={(ref) => {
                            this.activitiList = ref;
                            registerChild(ref);
                        }}
                        width={width}
                        height={height}
                        onRowsRendered={onRowsRendered}
                        rowCount={rowCount}
                        rowHeight={this.cache.rowHeight}
                        rowRenderer={this._rowRenderer}
                        overscanRowCount={3}
                        deferredMeasurementCache={this.cache}
                        />
                    )}
                </AutoSizer>
            </div>
        )}
</InfiniteLoader>