Бесконечная прокрутка с React JS


Я рассматриваю способы реализации бесконечной прокрутки с помощью React. Я наткнулся react-infinite-scroll и нашел его неэффективным, поскольку он просто добавляет узлы в DOM и не удаляет их. Есть ли какое-либо проверенное решение с React, которое будет добавлять, удалять и поддерживать постоянное количество узлов в DOM.

здесь jsfiddle проблема. В этой проблеме я хочу иметь только 50 элементов в DOM одновременно. другие должны быть загружены и удаляется при прокрутке пользователя вверх и вниз. Мы начали использовать React из-за его алгоритмов оптимизации. Теперь я не мог найти решение этой проблемы. Я наткнулся airbnb infinite js. Но он реализован с помощью Jquery. Чтобы использовать этот бесконечный свиток airbnb, я должен потерять оптимизацию реакции, которую я не хочу делать.

пример кода, который я хочу добавить scroll (здесь я загружаю все элементы. Моя цель состоит в том, чтобы загрузить только 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 67

4 ответа:

в основном при прокрутке вы хотите решить, какие элементы видны, а затем повторно отобразить только эти элементы, с одним элементом прокладки сверху и снизу, чтобы представить элементы за кадром.

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: Моделирование Двунаправленной Бесконечной Прокрутки.

проверьте наш React Infinite Libary:

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

Обновление Декабрь 2016

Я на самом деле использую react-virtualized во многих моих проектах в последнее время и найти, что он охватывает большинство случаев использования намного лучше. Обе библиотеки хороши, это зависит именно от того, что вы ищете. Например, реагировать виртуализации поддерживает переменную высоту JIT-компилятора измерения через специальный называется CellMeasurer, пример здесь https://bvaughn.github.io/react-virtualized/#/components/CellMeasurer.

Концепция Бесконечной Прокрутки С Использованием React Js


мы можем сделать бесконечную работу прокрутки, прослушивая событие прокрутки. Мы можем добавить прослушиватель событий к родительскому самому div или даже к объекту window.

взгляните на следующий код

   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", и вы можете получить доступ к этому div, используя это.рефери.iScroll.

убедитесь, что высота div меньше, чем общая высота основное отображение элементов иначе вы не получите полосу прокрутки. Вы можете установить высоту до 100% или использовать объект window вместо нашего 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 и отображает все элементы. Опять же, как это демо, мы показываем с помощью номера. В реальных приложениях вам нужно перебирать массив элементов и отображать значения, которые он содержит.

обновить функцию рендеринга сейчас:

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>
    );
} 

Да, это просто нормальный компонент, показывающий несколько лис. Как сделать его бесконечно прокручиваемым? Надеюсь, вы помните, что мы использовали ref с именем iScroll ранее, это вступает в действие сейчас.

componentDidMount() {
    this.refs.iScroll.addEventListener("scroll", () => {
      if (
        this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >=
        this.refs.iScroll.scrollHeight
      ) {
        this.loadMoreItems();
      }
    });
}

как вы все знаете, react components имеет функцию 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 будет отображаться (как показано в функции рендеринга). Затем вызывается функция 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;