Reactで無限スクロールを実装する方法を探しています。 react-infinite-scroll に出会いましたが、DOMにノードを追加するだけで削除しないため、効率が悪いことがわかりました。 DOMに一定数のノードを追加、削除、維持するReactを備えた実証済みのソリューションはありますか。
jsfiddle 問題です。この問題では、DOMに一度に50個の要素のみが必要です。その他は、ユーザーが上下にスクロールするときにロードおよび削除する必要があります。最適化アルゴリズムのため、Reactの使用を開始しました。今、私はこの問題の解決策を見つけることができませんでした。 airbnb infinite js に出会いました。ただし、Jqueryで実装されています。このairbnb無限スクロールを使用するには、したくないReact最適化を解除する必要があります。
スクロールを追加するサンプルコードは(ここではすべてのアイテムをロードしています。私の目標は一度に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'));
ヘルプを探しています...
基本的にスクロールするときは、表示する要素を決定し、それらの要素のみを表示するように再レンダリングします。上下に1つのスペーサー要素を配置して、オフスクリーン要素を表します。
Vjeux ここでフィドルを作成しました。これを見ることができます: jsfiddle 。
スクロールすると実行されます
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
});
},
レンダリング関数は、displayStart..displayEnd
の範囲の行のみを表示します。
ReactJS:双方向無限スクロールのモデリング にも興味があるかもしれません。
React Infinite Libraryをご覧ください。
https://github.com/seatgeek/react-infinite
2016年12月更新
私は実際に多くのプロジェクトで react-virtualized を実際に使用しており、ほとんどのユースケースをより適切にカバーしていることがわかりました。どちらのライブラリも優れています。探しているものによって異なります。たとえば、react-virtualizedは、CellMeasurer
と呼ばれるHOCを介した可変高さのJIT測定をサポートしています。例はこちら https://bvaughn.github.io/react-virtualized/#/components/CellMeasurer です。
2018年11月更新
React-virtualizedからの多くの教訓は、同じ著者のより小さく、より速く、より効率的な react-window ライブラリに移植されました。
React Jsを使用した無限スクロールの概念
Scrollイベントをリッスンすることにより、無限スクロールを機能させることができます。イベントリスナーは、親のdivまたはウィンドウオブジェクトのいずれかに追加できます。
次のコードを見てください
render() {
return (
<div
className="vc"
ref="iScroll"
style={{ height: "420px", overflow: "auto" }}
>
<h2>Hurrah! My First React Infinite Scroll</h2>
<ul>
</ul>
</div>
);
}
上記のコードには単純なulタグがあります。その中にフェッチされたアイテムをバインドします。このタグはdivで囲まれ、そこにイベントリスナーをアタッチしてスクロールイベントをリッスンします。
ここで使用されているすべてのタグを知っていることを願っています。じゃない?さて、一部の人はrefに慣れていないかもしれません!そのため、refは部門(div)または他のhtml要素への参照を定義するために使用されます。この参照を使用すると、reactでその要素を把握できます。参照に「iScroll」という名前を付けました。this.refs.iScrollを使用してこのdivにアクセスできます。
Divの高さがプライマリ表示アイテムの合計の高さよりも低いことを確認してください。そうでない場合、スクロールバーは表示されません。高さを100%に設定するか、iScroll divの代わりにウィンドウオブジェクトを使用して、ウィンドウレベルでスクロールできます。
次のようなコンストラクターについて説明しましょう。
constructor(props) {
super(props);
this.state = {
items: 10,
loadingState: false
};
}
この状態オブジェクトには2つのプロパティがあります。アイテムとloadingState。項目は、ulセクション内にlisとして含めることができる利用可能な項目の数を示し、データがロードされているときに、loadingStateがテキストの読み込みを表示するために使用されています。デモを提供しているだけなので、アイテムとして番号を使用したのはそのためです。実際のアプリケーションでは、おそらく実際のデータのリストをそこに保持します。
その後、すべてのアイテムをレンダリングする関数を作成します。
displayItems() {
var items = [];
for (var k = 0; k < this.state.items; k++) {
items.Push(<li key={k}>Item-VoidCanvas {k}</li>);
}
return items;
}
この関数は、liのリストを作成し、すべてのアイテムを表示します。繰り返しますが、これはデモなので、この番号を使用して表示しています。実際のアプリでは、items配列を反復処理し、それに含まれる値を表示する必要があります。
今すぐレンダリング機能を更新します。
render() {
return (
<div
className="vc"
ref="iScroll"
style={{ height: "200px", overflow: "auto" }}
>
<h2>Hurrah! My First React Infinite Scroll</h2>
<ul>
{this.displayItems()}
</ul>
{this.state.loadingState
? <p className="loading">
loading More Items..
</p>
: ""}
</div>
);
}
はい、これはわずかなlisを示す単なる通常のコンポーネントです。どのようにして無限にスクロール可能にしますか?以前にiScrollという名前のrefを使用したことを覚えておいてください。これが今すぐ有効になります。
componentDidMount() {
this.refs.iScroll.addEventListener("scroll", () => {
if (
this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >=
this.refs.iScroll.scrollHeight
) {
this.loadMoreItems();
}
});
}
ご存じのとおり、reactコンポーネントにはcomponentDidMount()関数があり、そのコンポーネントのテンプレートがDOMにレンダリングされると自動的に呼び出されます。また、同じ関数を使用して、スクロール用のイベントリスナーをdiv iScrollに追加しました。要素のscrollTopプロパティは、スクロール位置とclientHeightプロパティでそれを見つけます。次に、if条件は、これら2つのプロパティの追加がスクロールバーの高さ以上であるかどうかをチェックします。条件が真の場合、loadMoreItems関数が実行されます。
関数の外観は次のとおりです。
loadMoreItems() {
if(this.state.loadingState){
return;
}
this.setState({ loadingState: true });
// you may call ajax instead of setTimeout
setTimeout(() => {
this.setState({ items: this.state.items + 10, loadingState: false });
}, 1000);
}
とても簡単です。まず、loadingStateをtrueに設定すると、loading divが表示されます(render関数に示されているように)。次に、setTimeout関数が呼び出され、アイテムの数が10増えて、loadingStateが再びfalseになります。ここでsetTimeoutを使用する理由は、時間遅延を生成するためです。実際のアプリケーションでは、おそらくサーバーに対してajax呼び出しを行い、その解決時にsetTimeoutのコールバックによって行われる同様のことを実行します。ここに完全なコードスニペット:
class Layout extends React.Component {
constructor(props) {
super(props);
this.state = {
items: 10,
loadingState: false
};
}
componentDidMount() {
this.refs.iScroll.addEventListener("scroll", () => {
if (this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >= this.refs.iScroll.scrollHeight - 20){
this.loadMoreItems();
}
});
}
displayItems() {
var items = [];
for (var i = 0; i < this.state.items; i++) {
items.Push(<li key={i}>Item {i}</li>);
}
return items;
}
loadMoreItems() {
if(this.state.loadingState){
return;
}
this.setState({ loadingState: true });
setTimeout(() => {
this.setState({ items: this.state.items + 10, loadingState: false });
}, 1000);
}
render() {
return (
<div ref="iScroll" style={{ height: "200px", overflow: "auto" }}>
<ul>
{this.displayItems()}
</ul>
{this.state.loadingState ? <p className="loading"> loading More Items..</p> : ""}
</div>
);
}
}
ReactDOM.render(<Layout />, document.getElementById('example'));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="example"></div>
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;