Rolagem infinita com React JS

90

Estou procurando maneiras de implementar a rolagem infinita com React. Eu encontrei react-infinite-scroll e achei ineficiente, pois apenas adiciona nós ao DOM e não os remove. Existe alguma solução comprovada com React que irá adicionar, remover e manter um número constante de nós no DOM.

Aqui está o problema jsfiddle . Neste problema, quero ter apenas 50 elementos no DOM por vez. outros devem ser carregados e removidos conforme o usuário rola para cima e para baixo. Começamos a usar o React por causa de seus algoritmos de otimização. Agora eu não conseguia encontrar solução para este problema. Eu encontrei airbnb infinite js . Mas é implementado com Jquery. Para usar este scroll infinito airbnb, tenho que perder a otimização React, que não quero fazer.

o código de amostra que desejo adicionar à rolagem é (aqui estou carregando todos os itens. Meu objetivo é carregar apenas 50 itens por vez)

/** @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'));

Procurando ajuda ...

Rajeev
fonte

Respostas:

58

Basicamente, ao rolar, você deve decidir quais elementos são visíveis e, em seguida, renderizá-los novamente para exibir apenas esses elementos, com um único espaçador na parte superior e inferior para representar os elementos fora da tela.

Vjeux fez um violino aqui que você pode olhar: jsfiddle .

Ao rolar, ele executa

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

e a função de renderização exibirá apenas as linhas do intervalo displayStart..displayEnd.

Você pode também estar interessado em ReactJS: Modeling Bi-Directional Infinite Scrolling .

Sophie Alpert
fonte
2
Esta é uma ótima técnica ... thx! No entanto, ele falha quando recordHeight é diferente para cada linha. Estou experimentando uma solução para essa situação. Vou postar se conseguir fazer funcionar.
manalang
@manalang Você encontrou solução para alturas diferentes para cada linha?
Exceção de
1
Outro projeto para verificar é infinity.js (para inspiração). Se você tiver elementos de altura dinâmicos, poderá criar o conceito de uma "página", que é um conjunto de elementos na janela de visualização. Digamos que haja 3 elementos e o terceiro elemento é muito longo e se estende para fora da página. Então você pode, digamos, "altura da página" é o tamanho dos 3 elementos maiores. Em seguida, construa nós virtuais usando a menor altura de elemento. Então var count = pageHeight / minElementHeight. Portanto, você pode construir 50 elementos, embora apenas 3 sejam renderizados, mas isso ainda terá um bom desempenho.
Lance Pollard
14
Nada aparece no violino. Botões de geração aparecem, mas nada mais.
trovoada
3
@ sophie-alpert: É possível atualizar o jsfiddle? Eu sei que você estará ocupado, mas se você puder atualizá-lo, beneficiaria muitos como eu: D
João Samuel
26

Confira nossa biblioteca React Infinite:

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

Atualização de dezembro de 2016

Na verdade, tenho usado a virtualização reativa em muitos dos meus projetos recentemente e descobri que ela cobre muito melhor a maioria dos casos de uso. Ambas as bibliotecas são boas, depende exatamente do que você está procurando. Por exemplo, o react-virtualized suporta medição JIT de altura variável por meio de um HOC chamado CellMeasurer, exemplo aqui https://bvaughn.github.io/react-virtualized/#/components/CellMeasurer .

Atualização de novembro de 2018

Muitas das lições da virtualização reativa foram transferidas para a biblioteca de janela reativa menor, mais rápida e mais eficiente do mesmo autor.

Zach
fonte
@jos: use esta biblioteca. Isso removerá / anexará nós DOM conforme eles aparecem na janela de visualização.
wle8300 de
14
Esta biblioteca só funciona se você souber as alturas de seus elementos antes da renderização.
Druska
1
@Druska, Tecnicamente sim, no entanto, você também pode usar a janela como o contêiner de rolagem usando a opção useWindowAsScrollContainer.
HussienK
A biblioteca react-infinite suporta grades?
user1261710
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;
Sneh
fonte