使用React JS无限滚动

时间:2014-01-20 16:04:51

标签: javascript html infinite-scroll reactjs

我正在寻找使用React实现无限滚动的方法。我遇到react-infinite-scroll并发现它效率低,因为它只是向DOM添加节点而不删除它们。 React是否有任何经过验证的解决方案,它将在DOM中添加,删除和维护恒定数量的节点。

这是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'));

寻求帮助......

4 个答案:

答案 0 :(得分:54)

基本上在滚动时,您想要决定哪些元素是可见的,然后重新渲染以仅显示那些元素,在顶部和底部使用单个间隔元素来表示屏幕外元素。

Vjeux在这里做了一个小提琴你可以看一下:

http://jsfiddle.net/vjeux/KbWJ2/9/

滚动时执行

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: Modeling Bi-Directional Infinite Scrolling感兴趣。

答案 1 :(得分:23)

查看我们的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库。

答案 2 :(得分:16)

使用React Js的无限滚动概念

我们可以通过监听滚动事件来进行无限滚动工作。我们可以将事件监听器添加到父最大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元素的引用。通过使用此引用,您可以在反应中保持该元素。我们为引用指定了名称“iScroll”,您可以使用this.refs.iScroll访问此div。

确保div的高度小于主要显示项目的总高度,否则您将无法获得滚动条。您可以将高度设置为100%或使用窗口对象而不是我们的iScroll div来在窗口级别进行滚动。

现在让我们谈谈看起来像这样的构造函数:

constructor(props) {
    super(props);
    this.state = {
      items: 10,
      loadingState: false
    };
}

这里的状态对象有两个属性; items和loadingState。这些项表示可以作为lis包含在ul部分中的可用项目数,并且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的引用,现在就开始行动了。

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条件将检查这两个属性的添加是否大于或等于滚动条高度。如果条件为真,则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,然后显示加载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>

答案 3 :(得分: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;