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

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

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

пример кода я хочу добавить прокрутку (здесь я загружаю все элементы. Моя цель-загрузить только 50 предметов в a время)

/** @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 ответов


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

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 во многих моих проектах в последнее время и обнаруживают, что он охватывает большинство случаев использования намного лучше. Обе библиотеки хороши, это зависит от того, что именно вы ищете. Например, 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 используется для определения ссылки на division (div) или любой другой html-элемент. Используя эту ссылку, вы можете удержать этот элемент в react. Мы дали ссылку имя "iScroll", и вы можете получить доступ к этому div, используя это.рефери.iScroll.

убедитесь, что высота div меньше, чем общая высота первичный показ элементов, иначе вы не получите полосу прокрутки. Вы можете установить высоту 100% или использовать объект window вместо нашего iscroll div, чтобы сделать прокрутку на уровне окна.

теперь давайте поговорим о конструкторе, который будет выглядеть так:

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

в объекте state есть два свойства: 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 имеют функцию 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;