ReactJSによる無限スクロール


89

Reactで無限スクロールを実装する方法を探しています。私はreact-infinite-scrollに出くわしましたが、それはDOMにノードを追加するだけで、それらを削除しないため、非効率的であることがわかりました。DOMに一定数のノードを追加、削除、維持するReactの実証済みのソリューションはありますか?

これがjsfiddleの問題です。この問題では、一度にDOMに50個の要素のみを含めたいと思います。他のものは、ユーザーが上下にスクロールするときにロードおよび削除する必要があります。最適化アルゴリズムのため、Reactの使用を開始しました。今、私はこの問題の解決策を見つけることができませんでした。私はairbnbinfinitejsに出くわしました。ただし、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'));

ヘルプを探しています...

回答:


57

基本的に、スクロールするときは、表示する要素を決定してから再レンダリングして、それらの要素のみを表示します。上下に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:双方向無限スクロールのモデリングにも興味があるかもしれません。


2
これは素晴らしいテクニックです... thx!ただし、recordHeightが行ごとに異なる場合は失敗します。私はその状況の修正を試みています。動作するようになったら投稿します。
マナラン2014年

@manalang各行の高さを変えるための解決策を見つけましたか?
例外

1
チェックアウトするもう1つのプロジェクトは、infinity.js(インスピレーション用)です。動的な高さ要素がある場合は、ビューポートの要素のセットである「ページ」の概念を作成できます。3つの要素があり、3番目の要素が非常に長く、ページからはみ出しているとします。次に、「ページの高さ」は最大の3つの要素のサイズであると言うことができます。次に、最小の要素の高さを使用して仮想ノードを構築します。だからvar count = pageHeight / minElementHeight。したがって、レンダリングされる要素が3つしかない場合でも、50の要素を作成できますが、それでも優れたパフォーマンスが得られます。
ランスポラード2014

14
フィドルには何も表示されません。生成ボタンが表示されますが、それ以外は表示されません。
2015

3
@ sophie-alpert:jsfiddleを更新することは可能ですか?私はあなたが忙しくなることを知っています、しかしあなたがそれを更新することができれば、それは私のような多くの人に利益をもたらすでしょう:D
ジョンサミュエル

26

React InfiniteLibraryをご覧ください。

https://github.com/seatgeek/react-infinite

2016年12月の更新

私は最近、多くのプロジェクトで実際にreact-virtualizedを使用していますが、ほとんどのユースケースをより適切にカバーしていることがわかりました。どちらのライブラリも優れています。探しているものによって異なります。たとえば、react-virtualizedはCellMeasurerhttps: //bvaughn.github.io/react-virtualized/#/components/CellMeasurerというHOCを介した可変高さのJIT測定をサポートしています

2018年11月の更新

react-virtualizedからの多くの教訓は、同じ作者からのより小さく、より速く、より効率的なreact-windowライブラリに移植されました。


@jos:このライブラリを使用してください。ビューポートに表示されるDOMノードを削除/追加します。
wle8300 2016年

14
このライブラリは、レンダリングする前に要素の高さがわかっている場合にのみ機能します。
Druska 2016年

1
@ Druska、技術的にはそうですが、useWindowAsScrollContainerオプションを使用して、ウィンドウをスクロールコンテナとして使用することもできます。
hussienK 2016

react-infiniteライブラリはグリッドをサポートしていますか?
user1261710 2017


1
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;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.