I am looking at ways to implement infinite scrolling with React. I have come across react-infinite-scroll and found it inefficient as it just adds nodes to the DOM and doesn
Check out our React Infinite Library:
https://github.com/seatgeek/react-infinite
Update December 2016
I've actually been using react-virtualized in a lot of my projects recently and find that it covers the majority of use cases a lot better. Both libraries are good, it depends on exactly what you're looking for. For instance, react-virtualized supports variable height JIT measuring via an HOC called CellMeasurer
, example here https://bvaughn.github.io/react-virtualized/#/components/CellMeasurer.
Update November 2018
A lot of the lessons from react-virtualized have been ported to the smaller, faster, more efficient react-window library from the same author.
import React, { Component } from 'react';
import InfiniteScroll from 'react-infinite-scroller';
const api = {
baseUrl: '/joblist'
};
class Jobs extends Component {
constructor(props) {
super(props);
this.state = {
listData: [],
hasMoreItems: true,
nextHref: null
};
}
fetchData(){
var self = this;
var url = api.baseUrl;
if(this.state.nextHref) {
url = this.state.nextHref;
}
fetch(url)
.then( (response) => {
return response.json() })
.then( (json) => {
var list = self.state.listData;
json.data.map(data => {
list.push(data);
});
if(json.next_page_url != null) {
self.setState({
nextHref: resp.next_page_url,
listData: list
});
} else {
self.setState({
hasMoreItems: false
});
}
})
.catch(error => console.log('err ' + error));
}
}
componentDidMount() {
this.fetchData();
}
render() {
const loader = <div className="loader">Loading ...</div>;
let JobItems;
if(this.state.listData){
JobItems = this.state.listData.map(Job => {
return (
<tr>
<td>{Job.job_number}</td>
<td>{Job.title}</td>
<td>{Job.description}</td>
<td>{Job.status}</td>
</tr>
);
});
}
return (
<div className="Jobs">
<div className="container">
<h2>Jobs List</h2>
<InfiniteScroll
pageStart={0}
loadMore={this.fetchData.bind(this)}
hasMore={this.state.hasMoreItems}
loader={loader}>
<table className="table table-bordered">
<thead>
<tr>
<th>Job Number</th>
<th>Title</th>
<th>Description</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{JobItems}
</tbody>
</table>
</InfiniteScroll>
</div>
</div>
);
}
}
export default Jobs;
Basically when scrolling you want to decide which elements are visible and then rerender to display only those elements, with a single spacer element on top and bottom to represent the offscreen elements.
Vjeux made a fiddle here which you can look at: jsfiddle.
Upon scrolling it executes
scrollState: function(scroll) {
var visibleStart = Math.floor(scroll / this.state.recordHeight);
var visibleEnd = Math.min(visibleStart + this.state.recordsPerBody, this.state.total - 1);
var displayStart = Math.max(0, Math.floor(scroll / this.state.recordHeight) - this.state.recordsPerBody * 1.5);
var displayEnd = Math.min(displayStart + 4 * this.state.recordsPerBody, this.state.total - 1);
this.setState({
visibleStart: visibleStart,
visibleEnd: visibleEnd,
displayStart: displayStart,
displayEnd: displayEnd,
scroll: scroll
});
},
and then the render function will display only the rows in the range displayStart..displayEnd
.
You may also be interested in ReactJS: Modeling Bi-Directional Infinite Scrolling.