使用 React JS 进行无限滚动

我正在寻找用 React 实现无限滚动的方法。我遇到过 反应-无限-卷轴,发现它效率低下,因为它只是向 DOM 添加节点,而不删除它们。是否有任何已证实的解决方案与反应,将添加,删除和维护在 DOM 中的节点数量不变。

这是 Jsfiddle的问题。 在这个问题中,我希望在 DOM 中一次只有50个元素。其他的应该在用户向上和向下滚动时加载和删除。 我们已经开始使用反应,因为它的优化算法。现在我找不到解决这个问题的办法。我遇到了 Airbnb 无限 J。但它是用 Jquery 实现的。要使用这个 Airbnb 无限滚动,我必须失去反应优化,我不想这样做。

示例代码,我想添加滚动是(在这里我加载所有项目。我的目标是一次只装载50个项目)

/** @jsx React.DOM */


var Hello = React.createClass({
render: function() {
return (<li>Hello {this.props.name}</li>);
}
});


var HelloList = React.createClass({
getInitialState: function() {
var numbers =  [];
for(var i=1;i<10000;i++){
numbers.push(i);
}
return {data:numbers};
},


render: function(){
var response =  this.state.data.map(function(contact){
return (<Hello name="World"></Hello>);
});


return (<ul>{response}</ul>)
}
});


React.renderComponent(<HelloList/>, document.getElementById('content'));

寻求帮助。

91930 次浏览

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.

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;