Бесконечная прокрутка с 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;
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.