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

89

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

Вот проблема с jsfiddle . В этой задаче я хочу иметь только 50 элементов в DOM за раз. другие должны быть загружены и удалены по мере того, как пользователь прокручивает вверх и вниз. Мы начали использовать 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'));

Нужна помощь ...

Раджив
источник

Ответы:

57

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

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: Modeling Bi-Directional Infinite Scrolling .

Софи Альперт
источник
2
Это отличная техника ... спасибо! Однако это не удается, если recordHeight отличается для каждой строки. Я экспериментирую с исправлением этой ситуации. Я выложу, если заработает.
manalang
@manalang Вы нашли решение для разной высоты для каждой строки?
Исключение
1
Еще один проект, который стоит попробовать, - это infinity.js (для вдохновения). Если у вас есть элементы с динамической высотой, вы можете создать концепцию «страницы», которая представляет собой набор элементов в области просмотра. Скажем, есть 3 элемента, а третий очень длинный и выходит за пределы страницы. Тогда вы можете, скажем, «высота страницы» - это размер трех самых больших элементов. Затем создайте виртуальные узлы, используя наименьшую высоту элемента. Итак var count = pageHeight / minElementHeight. Таким образом, вы можете создать 50 элементов, хотя визуализируются только 3, но это все равно даст вам хорошую производительность.
Лэнс Поллард
14
На скрипке ничего не появляется. Появляются кнопки сгенерировать, но больше ничего.
чт
3
@ sophie-alpert: Можно ли обновить jsfiddle? Я знаю, что вы будете заняты, но если вы сможете обновить его, это принесет пользу многим вроде меня: D
Джон Самуэль
26

Ознакомьтесь с нашей библиотекой React Infinite:

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

Обновление декабрь 2016 г.

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

Обновление ноябрь 2018 г.

Многие уроки от react-virtualized были перенесены в меньшую, более быструю и более эффективную библиотеку окна реакции от того же автора.

Зак
источник
@jos: используйте эту библиотеку. Он будет удалять / добавлять узлы DOM по мере их появления в области просмотра.
wle8300
14
Эта библиотека работает, только если вы знаете высоту своих элементов перед рендерингом.
Druska
1
@Druska, Технически да, однако вы также можете использовать окно как контейнер прокрутки, используя опцию useWindowAsScrollContainer.
HussienK
Поддерживает ли библиотека react-infinite сетки?
user1261710 02
Это действительно так, github.com/bvaughn/react-virtualized/blob/master/docs/Grid.md
Зак
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;
Снех
источник