Scorrimento infinito con React JS


89

Sto cercando modi per implementare lo scorrimento infinito con React. Mi sono imbattuto in react-infinite-scroll e l'ho trovato inefficiente in quanto aggiunge solo nodi al DOM e non li rimuove. Esiste una soluzione collaudata con React che aggiungerà, rimuoverà e manterrà un numero costante di nodi nel DOM.

Ecco il problema di jsfiddle . In questo problema, voglio avere solo 50 elementi nel DOM alla volta. altri dovrebbero essere caricati e rimossi mentre l'utente scorre su e giù. Abbiamo iniziato a utilizzare React a causa dei suoi algoritmi di ottimizzazione. Ora non sono riuscito a trovare una soluzione a questo problema. Mi sono imbattuto in airbnb infinite js . Ma è implementato con Jquery. Per usare questo scorrimento infinito di airbnb, devo perdere l'ottimizzazione di React che non voglio fare.

il codice di esempio che voglio aggiungere è lo scorrimento (qui sto caricando tutti gli elementi. Il mio obiettivo è caricare solo 50 elementi alla volta)

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

In cerca di aiuto ...

Risposte:


57

Fondamentalmente durante lo scorrimento si desidera decidere quali elementi sono visibili e quindi eseguire nuovamente il rendering per visualizzare solo quegli elementi, con un singolo elemento spaziatore in alto e in basso per rappresentare gli elementi fuori schermo.

Vjeux ha creato un violino qui che puoi guardare: jsfiddle .

Dopo lo scorrimento viene eseguito

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 quindi la funzione di rendering visualizzerà solo le righe nell'intervallo displayStart..displayEnd.

Potresti anche essere interessato a ReactJS: Modeling Bi-Directional Infinite Scrolling .


2
Questa è una grande tecnica ... grazie! Tuttavia, non riesce quando recordHeight è diverso per ogni riga. Sto sperimentando una soluzione per quella situazione. Lo posterò se lo faccio funzionare.
Manalang

@manalang Hai trovato una soluzione per un'altezza diversa per ogni riga?
Eccezione

1
Un altro progetto da verificare è infinity.js (per l'ispirazione). Se disponi di elementi di altezza dinamici, puoi creare il concetto di "pagina" che è un insieme di elementi nella visualizzazione. Diciamo che ci sono 3 elementi e il terzo elemento è molto lungo e si estende fuori dalla pagina. Quindi puoi, ad esempio, "altezza pagina" è la dimensione dei 3 elementi più grandi. Quindi costruisci nodi virtuali utilizzando l' altezza dell'elemento più piccola . Quindi var count = pageHeight / minElementHeight. Quindi potresti costruire 50 elementi, anche se solo 3 sono renderizzati, ma questo ti darà comunque buone prestazioni.
Lance Pollard

14
Niente appare nel violino. Vengono visualizzati i pulsanti Genera, ma nient'altro.
tuono

3
@ sophie-alpert: è possibile aggiornare jsfiddle? So che sarai impegnato, ma se riesci ad aggiornarlo, ne trarrebbe beneficio molti come me: D
John Samuel,

26

Dai un'occhiata alla nostra libreria React Infinite:

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

Aggiornamento dicembre 2016

Recentemente ho utilizzato la virtualizzazione reattiva in molti dei miei progetti e ho scoperto che copre molto meglio la maggior parte dei casi d'uso. Entrambe le librerie sono buone, dipende esattamente da cosa stai cercando. Ad esempio, react-virtualized supporta la misurazione JIT ad altezza variabile tramite un HOC chiamato CellMeasurer, esempio qui https://bvaughn.github.io/react-virtualized/#/components/CellMeasurer .

Aggiornamento novembre 2018

Molte delle lezioni da React-Virtualized sono state portate nella libreria React-Window più piccola, più veloce ed efficiente dello stesso autore.


@jos: usa questa libreria. Rimuoverà / accoderà i nodi DOM come appaiono nella visualizzazione.
wle8300

14
Questa libreria funziona solo se conosci le altezze dei tuoi elementi prima del rendering.
Druska

1
@Druska, tecnicamente sì, tuttavia puoi anche utilizzare la finestra come contenitore di scorrimento utilizzando l'opzione useWindowAsScrollContainer.
HussienK

La libreria React-Infinite supporta le griglie?
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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.