web-dev-qa-db-fra.com

Défilement infini avec React JS

Je cherche des moyens d'implémenter le défilement infini avec React. J'ai rencontré react-infinite-scroll et 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 ajoute, supprime et maintient un nombre constant de nœuds dans le DOM.

Voici le problème jsfiddle . Dans ce problème, je veux n'avoir que 50 éléments dans le DOM à la fois. les autres doivent être chargés et supprimés au fur et à mesure que l'utilisateur défile. Nous avons commencé à utiliser React à cause de ses algorithmes d'optimisation. Je ne pouvais pas trouver de solution à ce problème. J'ai rencontré airbnb infinite js . Mais il est implémenté avec Jquery. Pour utiliser ce défilement infini airbnb, je dois perdre l’optimisation React que je ne veux pas faire.

exemple de code que je veux ajouter est 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'));

Vous cherchez de l'aide ...

82
Rajeev

En général, lors du défilement, vous voulez choisir les éléments visibles, puis les restituer pour afficher uniquement ces éléments, avec un seul élément d'espacement en haut et en bas pour représenter les éléments hors écran.

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

la fonction de rendu n'affichera que les lignes de la plage displayStart..displayEnd.

Vous pouvez également être intéressé par ReactJS: Modélisation du défilement infini bidirectionnel .

56
Sophie Alpert

Découvrez notre React Infinite Library:

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

Mise à jour en décembre 2016

En fait, j’ai récemment utilisé react-virtualized dans beaucoup de mes projets et j’ai constaté qu’il couvrait beaucoup mieux la majorité des cas d’utilisation. Les deux bibliothèques sont bonnes, cela dépend de ce que vous recherchez. Par exemple, react-virtualized prend en charge la mesure JIT de hauteur variable via un HOC appelé CellMeasurer, exemple ici https://bvaughn.github.io/react-virtualized/#/components/CellMeasurer .

Mise à jour de novembre 2018

Une grande partie des leçons tirées de react-virtualized ont été reportées dans la bibliothèque plus petite, plus rapide et plus efficace react-window du même auteur.

24
Zach

Concept de défilement infini utilisant React Js


Nous pouvons faire fonctionner le défilement infini en écoutant l'événement de défilement. Nous pouvons ajouter un écouteur d'événement à l'objet parent le plus div ou même à l'objet window.

Regardez le code suivant

   render() {
        return (
          <div
            className="vc"
            ref="iScroll"
            style={{ height: "420px", overflow: "auto" }}
          >
            <h2>Hurrah! My First React Infinite Scroll</h2>
            <ul>

            </ul>
          </div>
        );
    }

Le code ci-dessus a une simple balise ul; à l'intérieur duquel nous lierons les éléments récupérés. Cette balise est entourée d'un div sur lequel nous allons attacher un écouteur d'événement pour écouter l'événement de défilement.

J'espère que vous connaissez toutes les balises utilisées ici. N'est-ce pas? Certains d’entre vous ne sont peut-être pas familiers avec les arbitres! Donc, ref est utilisé pour définir la référence à la division (div) ou à tout autre élément html. En utilisant cette référence, vous pouvez maîtriser cet élément dans react. Nous avons donné à la référence un nom "iScroll" et vous pouvez accéder à cette div en utilisant this.refs.iScroll.

Assurez-vous que la hauteur de la div est inférieure à la hauteur totale des éléments principaux, sinon vous n’obtiendrez pas la barre de défilement. Vous pouvez définir la hauteur à 100% ou utiliser l'objet window à la place de notre div iScroll pour faire défiler l'écran au niveau de la fenêtre.

Parlons maintenant du constructeur qui ressemblera à ceci:

constructor(props) {
    super(props);
    this.state = {
      items: 10,
      loadingState: false
    };
}

Il y a deux propriétés dans l'objet state ici; articles et état de chargement. Les éléments indiquent le nombre d'éléments disponibles pouvant être inclus dans la section ul et le paramètre loadingState a été utilisé pour afficher un chargement de texte ... lorsque les données sont en cours de chargement. Comme nous ne faisons qu’une démo, c’est pourquoi nous avons utilisé un nombre comme éléments. En situation réelle, vous y trouverez probablement votre liste de données actuelle.

Après cela, vous créerez une fonction qui rendra tous les éléments:

displayItems() {
    var items = [];
    for (var k = 0; k < this.state.items; k++) {
      items.Push(<li key={k}>Item-VoidCanvas {k}</li>);
    }
    return items;
}

Cette fonction crée une liste de li et affiche tous les éléments. Comme il s’agit d’une démo, nous affichons le numéro. Dans les applications réelles, vous devez parcourir le tableau de vos éléments et afficher les valeurs qu'il contient.

Mettez à jour la fonction de rendu maintenant:

render() {
    return (
      <div
        className="vc"
        ref="iScroll"
        style={{ height: "200px", overflow: "auto" }}
      >
        <h2>Hurrah! My First React Infinite Scroll</h2>
        <ul>
          {this.displayItems()}
        </ul>
        {this.state.loadingState
          ? <p className="loading">
          loading More Items..
        </p>
          : ""}
      </div>
    );
} 

Oui, ceci est juste une composante normale montrant peu de lis. Comment cela fait-il défiler à l'infini? J'espère que vous vous souviendrez que nous avons utilisé un arbitre nommé iScroll plus tôt, cela entre en action maintenant.

componentDidMount() {
    this.refs.iScroll.addEventListener("scroll", () => {
      if (
        this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >=
        this.refs.iScroll.scrollHeight
      ) {
        this.loadMoreItems();
      }
    });
}

Comme vous le savez tous, les composants de réaction ont une fonction composantDidMount () qui est appelée automatiquement lorsque le modèle de ce composant est rendu dans le DOM. Et j'ai utilisé la même fonction pour ajouter l'écouteur d'événements à faire défiler dans notre div iScroll. La propriété scrollTop de l'élément trouvera la position de défilement et la propriété clientHeight. Ensuite, la condition if vérifiera si l'ajout de ces deux propriétés est supérieur ou égal à la hauteur de la barre de défilement. Si la condition est vraie, la fonction loadMoreItems sera exécutée.

Voici à quoi ressemblera la fonction:

loadMoreItems() {
    if(this.state.loadingState){
        return;
    }
    this.setState({ loadingState: true });
    // you may call ajax instead of setTimeout
    setTimeout(() => {
        this.setState({ items: this.state.items + 10, loadingState: false });
    }, 1000);
}

C’est assez simple, commencez par définir le paramètre loadingState sur true et le div de chargement sera affiché (comme indiqué dans la fonction de rendu). Ensuite, une fonction setTimeout est appelée, ce qui augmente le nombre d'éléments de 10 et rend le loadingState faux. Ici, la raison pour laquelle nous utilisons setTimeout est de générer un délai. Dans les applications réelles, vous ferez probablement un appel ajax sur votre serveur et, à la résolution de celui-ci, vous effectuerez une opération similaire, effectuée par le rappel de notre setTimeout. Extrait de code complet ici:

class Layout extends React.Component {
  constructor(props) {
   super(props);
   this.state = {
      items: 10,
      loadingState: false
    };
  }

  componentDidMount() {
    this.refs.iScroll.addEventListener("scroll", () => {
      if (this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >= this.refs.iScroll.scrollHeight - 20){
        this.loadMoreItems();
      }
    });
  }

  displayItems() {
    var items = [];
    for (var i = 0; i < this.state.items; i++) {
      items.Push(<li key={i}>Item {i}</li>);
    }
    return items;
  }

  loadMoreItems() {
         if(this.state.loadingState){
                 return;
         }
    this.setState({ loadingState: true });
    setTimeout(() => {
      this.setState({ items: this.state.items + 10, loadingState: false });
    }, 1000);
  }

  render() {
    return (
      <div ref="iScroll" style={{ height: "200px", overflow: "auto" }}>
        <ul>
          {this.displayItems()}
        </ul>

        {this.state.loadingState ? <p className="loading"> loading More Items..</p> : ""}

      </div>
    );
  }
}

ReactDOM.render(<Layout />, document.getElementById('example'));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="example"></div>
16
Aniket Jha
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;
1
Sneh