问题
My _loadMoreRows and _isRowLoaded are not getting called, so loadedRowsMap remains empty and I am unable to identify the rows loaded to avoid making HTTP request .
Here's my complete code :
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;
As you can see I am making API call at didMount phase and therefore populating my redux store with the data. Data is coming fine. But isRowLoaded and loadMoreRows are not getting called.
I have debugged the sample code of infiniteLoader.example.js, there during the inital render those two functions are called and properly set loadedRowsMap.
What I am doing wrong here ? :-( Any help would be greatly appreciated .
回答1:
(The code you've posted has a lot going on; you'll probably get better responses if you remove the parts that aren't related to your question.)
My advice is to take a look at the values you're getting for list.length
, which you're passing to InfiniteLoader's rowCount
prop. InfiniteLoader will only call loadMoreRows
if rowCount
is higher than the number of rows it has data for.
For example: during the first render, the value is 0, because you have not fetched any data yet. This prevents a call to loadMoreRows
during the first render.
I also noticed that you are keeping loadedRowCount
and loadingRowCount
in state, but you never use them for anything. I don't think that's related to your issue, but it's also probably not intentional.
回答2:
I have resolved the issue after making some changes.
const mapStateToProps = (state) => {
return {
myList: state.dashboardReducer.myList
}
}
const list = this.props.myList.length ? this.props.recentActList : [];
const rowCount = list.length + (moreToBeLoaded ? 1 : 0);
To know why 1 more row loaded pls check this post react-virtualized InfiniteLoader/List - working example using AJAX
So the issue in my code was I was writing my custom onRowsRendered function and it was not handling the response correctly. Now I changed the code to use the one passed by InfiniteLoader.
Hope that helps.
<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>
来源:https://stackoverflow.com/questions/53446274/isrowloaded-and-loadmorerows-not-getting-called-react-virtualized