Défilement infini avec React JS


89

Je cherche des moyens d'implémenter le défilement infini avec React. J'ai rencontré react-infinite-scroll et je l'ai trouvé inefficace car il ajoute simplement des nœuds au DOM et ne les supprime pas. Existe-t-il une solution éprouvée avec React qui ajoutera, supprimera et maintiendra un nombre constant de nœuds dans le DOM.

Voici le problème de jsfiddle . Dans ce problème, je veux avoir seulement 50 éléments dans le DOM à la fois. d'autres doivent être chargés et supprimés lorsque l'utilisateur fait défiler vers le haut et vers le bas. Nous avons commencé à utiliser React en raison de ses algorithmes d'optimisation. Maintenant, je n'ai pas trouvé de solution à ce problème. Je suis tombé sur airbnb infinite js . Mais il est implémenté avec Jquery. Pour utiliser ce scroll infini airbnb, je dois perdre l'optimisation React, ce que je ne veux pas faire.

le code exemple que je veux ajouter est le scroll (ici, je charge tous les éléments. Mon objectif est de ne charger que 50 éléments à la fois)

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

Besoin d'aide ...

Réponses:


57

Fondamentalement, lors du défilement, vous voulez décider quels éléments sont visibles, puis effectuer un rendu pour n'afficher que ces éléments, avec un seul élément d'espacement en haut et en bas pour représenter les éléments hors écran.

Vjeux a fait ici un violon que vous pouvez regarder: jsfiddle .

Lors du défilement, il s'exécute

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

puis la fonction de rendu affichera uniquement les lignes de la plage displayStart..displayEnd.

Vous pouvez également être intéressé par ReactJS: Modélisation bi-directionnel infini Scrolling .


2
C'est une excellente technique ... merci! Cependant, il échoue lorsque recordHeight est différent pour chaque ligne. J'expérimente une solution pour cette situation. Je le posterai si je le fais fonctionner.
manalang le

@manalang Avez-vous trouvé une solution pour différentes hauteurs pour chaque rangée?
Exception

1
Un autre projet à vérifier est infinity.js (pour l'inspiration). Si vous avez des éléments de hauteur dynamiques, vous pouvez créer le concept d'une "page" qui est un ensemble d'éléments dans la fenêtre. Disons qu'il y a 3 éléments et que le troisième élément est super long et s'étend sur la page. Ensuite, vous pouvez, par exemple, "hauteur de la page" correspond à la taille des 3 plus grands éléments. Construisez ensuite des nœuds virtuels en utilisant la plus petite hauteur d'élément. Alors var count = pageHeight / minElementHeight. Vous pouvez donc construire 50 éléments, même si seulement 3 sont rendus, mais cela vous donnera toujours de bonnes performances.
Lance Pollard

14
Rien n'apparaît dans le violon. Les boutons Générer apparaissent, mais rien d'autre.
jeudi

3
@ sophie-alpert: Est-il possible de mettre à jour jsfiddle? Je sais que vous serez occupé, mais si vous pouvez le mettre à jour, cela profitera à beaucoup comme moi: D
John Samuel

26

Consultez notre bibliothèque React Infinite:

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

Mise à jour décembre 2016

J'ai récemment utilisé react-virtualized dans beaucoup de mes projets et je trouve que cela couvre beaucoup mieux la majorité des cas d'utilisation. Les deux bibliothèques sont bonnes, cela dépend exactement de ce que vous recherchez. Par exemple, react-virtualized prend en charge la mesure JIT à hauteur variable via un HOC appelé CellMeasurer, exemple ici https://bvaughn.github.io/react-virtualized/#/components/CellMeasurer .

Mise à jour novembre 2018

Un grand nombre des leçons de react-virtualized ont été portées dans la bibliothèque react-window plus petite, plus rapide et plus efficace du même auteur.


@jos: utilisez cette bibliothèque. Il supprimera / ajoutera les nœuds DOM tels qu'ils apparaissent dans la fenêtre.
wle8300

14
Cette bibliothèque ne fonctionne que si vous connaissez les hauteurs de vos éléments avant le rendu.
Druska

1
@Druska, techniquement oui, mais vous pouvez également utiliser la fenêtre comme conteneur de défilement en utilisant l'option useWindowAsScrollContainer.
HussienK

La bibliothèque react-infinite prend-elle en charge les grilles?
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;
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.