One place for hosting & domains

      Pagination

      Comment créer une pagination personnalisée avec React


      Introduction

      Nous nous impliquons souvent dans la construction d’applications web où nous devons récupérer de grands ensembles de données à partir d’un serveur distant, d’une API ou d’une base de données. Si vous mettez en place un système de paiement, par exemple, il peut aller chercher des milliers de transactions. S’il s’agit d’une app de réseaux sociaux, elle peut récupérer de nombreux commentaires, profils ou activités d’utilisateurs. Quoi qu’il en soit, il existe plusieurs solutions pour présenter les données d’une manière qui ne submerge pas l’utilisateur final qui interagit avec l’app.

      Une méthode pour traiter de grands ensembles de données consiste à utiliser la pagination. La pagination fonctionne efficacement lorsque vous connaissez à l’avance la taille de l’ensemble de données (le nombre total d’enregistrements contenu dans l’ensemble de données). Deuxièmement, vous ne chargez que le morceau de données requis de l’ensemble des données en fonction de l’interaction de l’utilisateur final avec le contrôle de pagination. C’est la technique utilisée pour afficher les résultats de recherche dans Google Search.

      Dans ce tutoriel, vous apprendrez comment construire un composant de pagination personnalisé avec React pour la pagination de grands ensembles de données. Vous construirez une vue paginée des pays du monde – un ensemble de données dont la taille est connue.

      Voici une démo de ce que vous allez construire dans ce tutoriel :

      Capture d'écran de l'application de démo - montrant les pays du monde

      Conditions préalables

      Pour suivre ce tutoriel, vous aurez besoin de :

      Ce tutoriel a été vérifié avec Node v14.2.0, npm v6.14.4, react v16.13.1, et react-scripts v3.4.1.

      Étape 1 – Mettre en place le projet

      Lancez une nouvelle application React en utilisant la commande create-react-app. Vous pouvez nommer l’application comme vous le souhaitez, mais ce tutoriel lui donnera le nom react-pagination :

      • npx create-react-app react-pagination

      Ensuite, vous installerez les dépendances nécessaires pour votre application. Tout d’abord, utilisez la fenêtre du terminal pour accéder au répertoire du projet :

      Exécutez la commande suivante pour installer les dépendances requises :

      • npm install bootstrap@4.1.0 prop-types@15.6.1 react-flags@0.1.13 countries-api@2.0.1 node-sass@4.14.1

      Vous installerez alors bootstrap, prop-types, react-flags, countries-api et node-sass.

      Vous avez installé le bootstrap comme une dépendance de votre demande, car vous aurez besoin d’un style par défaut. Vous utiliserez également les styles du composant de pagination Bootstrap.

      Pour inclure Bootstrap dans l’application, modifiez le fichier src/index.js :

      Et ajoutez la ligne suivante avant les autres déclarations d’import :

      src/index.js

      import "bootstrap/dist/css/bootstrap.min.css";
      

      Maintenant, le style Bootstrap sera disponible dans toute votre application.

      Vous avez également installé react-flags comme une dépendance de votre application. Afin d’accéder aux icônes des drapeaux à partir de votre application, vous devez copier les images des icônes dans le répertoire public de votre application.

      Créez un répertoire img dans votre répertoire public :

      Copiez les fichiers images dans flags vers img :

      • cp -R node_modules/react-flags/vendor/flags public/img

      Ce document fournit une copie de tous les react-flag à votre application.

      Maintenant que vous avez inclus quelques dépendances, lancez l’application en exécutant la commande suivante avec npm de la pagination du répertoire du projet react-pagination :

      Maintenant que vous avez lancé l’application, le développement peut commencer. Vous remarquerez qu’un onglet du navigateur a été ouvert pour vous avec la fonctionnalité de rechargement live pour rester en phase avec l’application au fur et à mesure de son développement.

      À ce stade, la vue de l’application devrait ressembler à la capture d’écran suivante :

      Vue initiale - Bienvenue sur React Screen

      Vous êtes maintenant prêt à commencer à créer des composants.

      Étape 2 – Créer le composant CountryCard

      Dans cette étape, vous allez créer le composant CountryCard. Le composant CountryCard génère le nom, la région et le drapeau d’un pays donné.

      Tout d’abord, créons un composant dans le répertoire src :

      Ensuite, créez une nouvelle CountryCard.js dans le répertoire src/components :

      • nano src/components/CountryCard.js

      Et ajoutez-y le code suivant :

      src/components/CountryCard.js

      import React from 'react';
      import PropTypes from 'prop-types';
      import Flag from 'react-flags';
      
      const CountryCard = props => {
        const {
          cca2: code2 = '', region = null, name = {}
        } = props.country || {};
      
        return (
          <div className="col-sm-6 col-md-4 country-card">
            <div className="country-card-container border-gray rounded border mx-2 my-3 d-flex flex-row align-items-center p-0 bg-light">
              <div className="h-100 position-relative border-gray border-right px-2 bg-white rounded-left">
                <Flag country={code2} format="png" pngSize={64} basePath="./img/flags" className="d-block h-100" />
              </div>
              <div className="px-3">
                <span className="country-name text-dark d-block font-weight-bold">{ name.common }</span>
                <span className="country-region text-secondary text-uppercase">{ region }</span>
              </div>
            </div>
          </div>
        )
      }
      
      CountryCard.propTypes = {
        country: PropTypes.shape({
          cca2: PropTypes.string.isRequired,
          region: PropTypes.string.isRequired,
          name: PropTypes.shape({
            common: PropTypes.string.isRequired
          }).isRequired
        }).isRequired
      };
      
      export default CountryCard;
      

      Le composant CountryCard nécessite un prop country qui contient les données sur le pays à afficher. Comme on l’a vu dans le module propTypes pour le composant CountryCard, l’objet prop country doit contenir les données suivantes :

      • cca2 – un code de pays à deux chiffres
      • region – la région du pays (par exemple, « Africa »)
      • name.common – le nom commun du pays (par exemple, « Nigeria »)

      Voici un exemple d’objet country :

      {
        cca2: "NG",
        region: "Africa",
        name: {
          common: "Nigeria"
        }
      }
      

      Notez également comment vous rendez le drapeau du pays en utilisant le paquet react-flags. Vous pouvez consulter la documentation react-flags pour en savoir plus sur les props nécessaires et sur la façon d’utiliser le paquet.

      Vous avez maintenant un composant CountryCard complété. Enfin, vous utiliserez CountryCard plusieurs fois pour afficher dans votre application différents drapeaux et différentes informations sur le pays.

      Dans cette étape, vous créerez le composant Pagination. Le composant Pagination contient la logique de construction, de rendu et de commutation des pages sur le contrôle de pagination.

      Ensuite, créez une nouvelle Pagination.js dans le répertoire src/components :

      • nano src/components/Pagination.js

      Et ajoutez-y le code suivant :

      src/components/Pagination.js

      import React, { Component, Fragment } from 'react';
      import PropTypes from 'prop-types';
      
      class Pagination extends Component {
        constructor(props) {
          super(props);
          const { totalRecords = null, pageLimit = 30, pageNeighbours = 0 } = props;
      
          this.pageLimit = typeof pageLimit === 'number' ? pageLimit : 30;
          this.totalRecords = typeof totalRecords === 'number' ? totalRecords : 0;
      
          // pageNeighbours can be: 0, 1 or 2
          this.pageNeighbours = typeof pageNeighbours === 'number'
            ? Math.max(0, Math.min(pageNeighbours, 2))
            : 0;
      
          this.totalPages = Math.ceil(this.totalRecords / this.pageLimit);
      
          this.state = { currentPage: 1 };
        }
      }
      
      Pagination.propTypes = {
        totalRecords: PropTypes.number.isRequired,
        pageLimit: PropTypes.number,
        pageNeighbours: PropTypes.number,
        onPageChanged: PropTypes.func
      };
      
      export default Pagination;
      

      Le volet Pagination peut prendre quatre props spéciaux, comme indiqué dans l’objet propTypes.

      • onPageChanged est une fonction appelée avec les données de l’état de pagination actuel uniquement lorsque la page en cours change.
      • totalRecords indique le nombre total de documents à paginer. C’est un obligatoire.
      • pageLimit indique le nombre d’enregistrements à afficher par page. Si elle n’est pas spécifiée, la valeur par défaut est 30 tel que défini dans la constructor().
      • pageNeighbours indique le nombre de numéros de page supplémentaires à afficher de chaque côté de la page en cours. La valeur minimale est 0 et la valeur maximale est 2. Si elle n’est pas spécifiée, la valeur par défaut est 0, comme définie dans la fonction constructor().

      L’image suivante illustre l’effet des différentes valeurs du prop pageNeighbours :

      Illustration de la page Neighbours

      Dans la fonction constructor(), vous calculez le nombre total de pages comme suit :

      this.totalPages = Math.ceil(this.totalRecords / this.pageLimit);
      

      Vous remarquerez que vous utilisez la fonctionMath.ceil() ici pour vous assurer que vous obtenez une valeur entière pour le nombre total de pages. Cela garantit également que les documents excédentaires soient saisis à la dernière page, en particulier dans les cas où le nombre de documents excédentaires est inférieur au nombre de documents à afficher par page.

      Enfin, vous avez initialisé l’état avec la propriété currentPage réglée sur 1. Vous avez besoin de cette propriété d’état pour garder une trace de la page actuellement active en interne.

      Ensuite, vous créerez la méthode pour générer les numéros de page.

      Après les importations mais avant la classe Pagination, ajoutez les constantes et la fonction de plage suivantes :

      src/components/Pagination.js

      // ...
      
      const LEFT_PAGE = 'LEFT';
      const RIGHT_PAGE = 'RIGHT';
      
      /**
       * Helper method for creating a range of numbers
       * range(1, 5) => [1, 2, 3, 4, 5]
       */
      const range = (from, to, step = 1) => {
        let i = from;
        const range = [];
      
        while (i <= to) {
          range.push(i);
          i += step;
        }
      
        return range;
      }
      

      Dans la classe Pagination, après la fonction constructor, ajoutez la méthode fetchPageNumbers qui suit :

      src/components/Pagination.js

      class Pagination extends Component {
        // ...
      
        /**
         * Let's say we have 10 pages and we set pageNeighbours to 2
         * Given that the current page is 6
         * The pagination control will look like the following:
         *
         * (1) < {4 5} [6] {7 8} > (10)
         *
         * (x) => terminal pages: first and last page(always visible)
         * [x] => represents current page
         * {...x} => represents page neighbours
         */
        fetchPageNumbers = () => {
          const totalPages = this.totalPages;
          const currentPage = this.state.currentPage;
          const pageNeighbours = this.pageNeighbours;
      
          /**
           * totalNumbers: the total page numbers to show on the control
           * totalBlocks: totalNumbers + 2 to cover for the left(<) and right(>) controls
           */
          const totalNumbers = (this.pageNeighbours * 2) + 3;
          const totalBlocks = totalNumbers + 2;
      
          if (totalPages > totalBlocks) {
            const startPage = Math.max(2, currentPage - pageNeighbours);
            const endPage = Math.min(totalPages - 1, currentPage + pageNeighbours);
            let pages = range(startPage, endPage);
      
            /**
             * hasLeftSpill: has hidden pages to the left
             * hasRightSpill: has hidden pages to the right
             * spillOffset: number of hidden pages either to the left or to the right
             */
            const hasLeftSpill = startPage > 2;
            const hasRightSpill = (totalPages - endPage) > 1;
            const spillOffset = totalNumbers - (pages.length + 1);
      
            switch (true) {
              // handle: (1) < {5 6} [7] {8 9} (10)
              case (hasLeftSpill && !hasRightSpill): {
                const extraPages = range(startPage - spillOffset, startPage - 1);
                pages = [LEFT_PAGE, ...extraPages, ...pages];
                break;
              }
      
              // handle: (1) {2 3} [4] {5 6} > (10)
              case (!hasLeftSpill && hasRightSpill): {
                const extraPages = range(endPage + 1, endPage + spillOffset);
                pages = [...pages, ...extraPages, RIGHT_PAGE];
                break;
              }
      
              // handle: (1) < {4 5} [6] {7 8} > (10)
              case (hasLeftSpill && hasRightSpill):
              default: {
                pages = [LEFT_PAGE, ...pages, RIGHT_PAGE];
                break;
              }
            }
      
            return [1, ...pages, totalPages];
          }
      
          return range(1, totalPages);
        }
      }
      

      Ici, vous définissez d’abord deux constantes : LEFT_PAGE et RIGHT_PAGE. Ces constantes seront utilisées pour indiquer les points où vous avez des contrôles de page pour vous déplacer respectivement à gauche et à droite.

      Vous avez également défini une aide avec la fonctionrange() qui peut vous aider à générer des séries de chiffres.

      Note : Si vous utilisez une bibliothèque d’utilité comme Lodashdans votre projet, vous pouvez alors utiliser la fonction _.range() fournie par Lodash à la place. L’extrait de code suivant montre la différence entre la fonction range() que vous venez de définir et celle de Lodash :

      range(1, 5); // returns [1, 2, 3, 4, 5]
      _.range(1, 5); // returns [1, 2, 3, 4]
      

      Ensuite, vous avez défini la méthode fetchPageNumbers() dans la classe Pagination. Cette méthode gère la logique de base pour générer les numéros de page à afficher sur le contrôle de pagination. Vous voulez que la première page et la dernière page soient toujours visibles.

      Tout d’abord, vous avez défini quelques variables. totalNumbers représente le nombre total de pages qui seront affichées sur le contrôle. totalBlocks représente le nombre total de pages à afficher, plus deux blocs supplémentaires pour les indicateurs de page gauche et droite.

      Si le nombre totalPages n’est pas supérieur à totalBlocks, vous renvoyez une série de chiffres allant de 1 à totalPages. Sinon, vous renvoyez le tableau des numéros de page, avec LEFT_PAGE et RIGHT_PAGE aux endroits où vous avez des pages qui débordent respectivement à gauche et à droite.

      Toutefois, notez que votre contrôle de pagination garantit que la première page et la dernière page sont toujours visibles. Les contrôles des pages gauche et droite apparaissent vers l’intérieur.

      Maintenant, vous allez ajouter la fonction render() pour vous permettre de contrôler la pagination.

      Dans la classe Pagination, après la méthode constructor et fetchPageNumbers, ajoutez la méthode render suivante :

      src/components/Pagination.js

      class Pagination extends Component {
        // ...
      
        render() {
          if (!this.totalRecords || this.totalPages === 1) return null;
      
          const { currentPage } = this.state;
          const pages = this.fetchPageNumbers();
      
          return (
            <Fragment>
              <nav aria-label="Countries Pagination">
                <ul className="pagination">
                  { pages.map((page, index) => {
      
                    if (page === LEFT_PAGE) return (
                      <li key={index} className="page-item">
                        <a className="page-link" href="https://www.digitalocean.com/community/tutorials/#" aria-label="Previous" onClick={this.handleMoveLeft}>
                          <span aria-hidden="true">&laquo;</span>
                          <span className="sr-only">Previous</span>
                        </a>
                      </li>
                    );
      
                    if (page === RIGHT_PAGE) return (
                      <li key={index} className="page-item">
                        <a className="page-link" href="https://www.digitalocean.com/community/tutorials/#" aria-label="Next" onClick={this.handleMoveRight}>
                          <span aria-hidden="true">&raquo;</span>
                          <span className="sr-only">Next</span>
                        </a>
                      </li>
                    );
      
                    return (
                      <li key={index} className={`page-item${ currentPage === page ? ' active' : ''}`}>
                        <a className="page-link" href="https://www.digitalocean.com/community/tutorials/#" onClick={ this.handleClick(page) }>{ page }</a>
                      </li>
                    );
      
                  }) }
      
                </ul>
              </nav>
            </Fragment>
          );
        }
      }
      

      Ici, vous générez le tableau des numéros de page en appelant la fonction fetchPageNumbers() que vous avez créée précédemment. Vous générez ensuite chaque numéro de page en utilisant Array.prototype.map(). Notez que vous enregistrez des gestionnaires d’événements de clic sur chaque numéro de page généré pour gérer les clics.

      Notez également que le contrôle de la pagination ne sera pas effectué si le proptotalRecords n’a pas été correctement transmis au composant Pagination ou dans les cas où il n’y a qu’une page 1.

      Enfin, vous définirez les méthodes de gestion des événements.

      Dans la classe Pagination, après la méthode con fetchPageNumbers et la méthode render, ajoutez ce qui suit :

      src/components/Pagination.js

      class Pagination extends Component {
        // ...
      
        componentDidMount() {
          this.gotoPage(1);
        }
      
        gotoPage = page => {
          const { onPageChanged = f => f } = this.props;
          const currentPage = Math.max(0, Math.min(page, this.totalPages));
          const paginationData = {
            currentPage,
            totalPages: this.totalPages,
            pageLimit: this.pageLimit,
            totalRecords: this.totalRecords
          };
      
          this.setState({ currentPage }, () => onPageChanged(paginationData));
        }
      
        handleClick = page => evt => {
          evt.preventDefault();
          this.gotoPage(page);
        }
      
        handleMoveLeft = evt => {
          evt.preventDefault();
          this.gotoPage(this.state.currentPage - (this.pageNeighbours * 2) - 1);
        }
      
        handleMoveRight = evt => {
          evt.preventDefault();
          this.gotoPage(this.state.currentPage + (this.pageNeighbours * 2) + 1);
        }
      }
      

      Vous définissez la méthode gotoPage() qui modifie l’état et met currentPage à la page spécifiée. Elle garantit que l’argument de la page a une valeur minimale de 1 et une valeur maximale du nombre total de pages. Il appelle enfin la fonction onPageChanged() qui a été transmise comme prop, avec des données indiquant le nouvel état de pagination.

      Lorsque le composant est monté, vous passez à la première page en appelant this.gotoPage(1) comme indiqué dans la méthode de cycle de vie componentDidMount().

      Remarquez comment vous utilisez (this.pageNeighbours * 2) dans handleMoveLeft() et handleMoveRight() pour faire glisser les numéros de page vers la gauche et vers la droite, respectivement, en fonction du numéro de page en cours.

      Voici une démonstration de l’interaction du mouvement de gauche à droite.

      Mouvement gauche-droite de l'interaction

      Vous avez maintenant terminé le composant Pagination. Les utilisateurs pourront interagir avec les commandes de navigation de ce composant pour afficher différentes pages de drapeaux.

      Étape 4 – Création du composant App

      Maintenant que vous avez un composant CountryCard et Pagination, vous pouvez les utiliser dans votre composant App.

      Modifiez le fichier App.js dans le répertoire src :

      Remplacez le contenu de App.js par les lignes de code suivantes :

      src/App.js

      import React, { Component } from 'react';
      import Countries from 'countries-api';
      import './App.css';
      import Pagination from './components/Pagination';
      import CountryCard from './components/CountryCard';
      
      class App extends Component {
        state = { allCountries: [], currentCountries: [], currentPage: null, totalPages: null }
      
        componentDidMount() {
          const { data: allCountries = [] } = Countries.findAll();
          this.setState({ allCountries });
        }
      
        onPageChanged = data => {
          const { allCountries } = this.state;
          const { currentPage, totalPages, pageLimit } = data;
          const offset = (currentPage - 1) * pageLimit;
          const currentCountries = allCountries.slice(offset, offset + pageLimit);
      
          this.setState({ currentPage, currentCountries, totalPages });
        }
      }
      
      export default App;
      

      Ici, vous initialisez l’état du composant App avec les attributs suivants :

      • allCountries – Voici un tableau de tous les pays qui figurent dans votre app. Initialisé vers un tableau vide ([]).
      • currentCountries – Il s’agit d’un tableau de tous les pays qui doivent être affichés sur la page actuellement active. Initialisé vers un tableau vide ([]).
      • currentPage – Le numéro de page de la page actuellement active. Initialisé vers la valeur null.
      • totalPages – Le nombre total de pages pour l’ensemble des fiches pays. Initialisé vers la valeur null.

      Ensuite, dans la méthode du cycle de vie de componentDidMount(), vous récupérez tous les pays du monde en utilisant le paquet countries-api en invoquant Countries.findAll(). Vous mettez ensuite à jour l’état de l’application, en définissant allCountries pour contenir tous les pays du monde. Vous pouvez voir la documentation countries-apipour en savoir plus sur le paquet.

      Enfin, vous avez défini la méthode onPageChanged() qui sera appelée chaque fois que vous naviguerez vers une nouvelle page à partir du contrôle de pagination. Cette méthode sera transmise à la prop onPageChanged du composant Pagination.

      Il y a deux lignes auxquelles il convient de prêter attention dans cette méthode. La première est cette ligne :

      const offset = (currentPage - 1) * pageLimit;
      

      La valeur offset indique l’index de départ pour récupérer les enregistrements de la page en cours. En utilisant (currentPage - 1), on s’assure que la valeur offset est basée sur zéro. Disons, par exemple, que vous affichez 25 enregistrements par page, et que vous êtes en train de consulter la page 5. La valeur offset sera alors de ((5 - 1) * 25 = 100).

      Par exemple, si vous récupérez des enregistrements sur demande à partir d’une base de données, voici un exemple de requête SQL pour vous montrer comment utiliser la valeur offset :

      SELECT * FROM `countries` LIMIT 100, 25
      

      Comme vous ne récupérez pas les enregistrements d’une base de données ou d’une source externe, vous avez besoin d’un moyen d’extraire le bloc d’enregistrements requis pour la page en cours.

      La deuxième est cette ligne :

      const currentCountries = allCountries.slice(offset, offset + pageLimit);
      

      Ici, vous utilisez la méthode Array.prototype.slice() pour extraire le bloc d’enregistrements requis de allCountries en passant la valeur offset comme indice de départ de la tranche et (offset + pageLimit) comme indice avant lequel la tranche doit se terminer.

      Note : Dans ce tutoriel, vous n’avez pas récupéré d’enregistrements provenant d’une source externe. Dans une véritable application, vous irez probablement chercher des enregistrements dans une base de données ou une API. La logique de récupération des enregistrements peut être consultée dans la méthodeonPageChanged() du composant App.

      Supposons que vous ayez un point final API fictif /api/countries?page={current_page}&limit={page_limit}. L’extrait suivant montre comment vous pouvez récupérer des pays à la demande à partir de l’API en utilisant le paquet HTTP [axios][“axios] :

      onPageChanged = data => {
        const { currentPage, totalPages, pageLimit } = data;
      
        axios.get(`/api/countries?page=${currentPage}&limit=${pageLimit}`)
          .then(response => {
            const currentCountries = response.data.countries;
            this.setState({ currentPage, currentCountries, totalPages });
          });
      }
      

      Maintenant, vous pouvez achever le composantApp en ajoutant la méthode render().

      Dans la classe App, mais après componentDidMount et onPageChanged, ajoutez la méthode render suivante :

      src/App.js

      class App extends Component {
        // ... other methods here ...
      
        render() {
          const { allCountries, currentCountries, currentPage, totalPages } = this.state;
          const totalCountries = allCountries.length;
      
          if (totalCountries === 0) return null;
      
          const headerClass = ['text-dark py-2 pr-4 m-0', currentPage ? 'border-gray border-right' : ''].join(' ').trim();
      
          return (
            <div className="container mb-5">
              <div className="row d-flex flex-row py-5">
                <div className="w-100 px-4 py-5 d-flex flex-row flex-wrap align-items-center justify-content-between">
                  <div className="d-flex flex-row align-items-center">
                    <h2 className={headerClass}>
                      <strong className="text-secondary">{totalCountries}</strong> Countries
                    </h2>
                    { currentPage && (
                      <span className="current-page d-inline-block h-100 pl-4 text-secondary">
                        Page <span className="font-weight-bold">{ currentPage }</span> / <span className="font-weight-bold">{ totalPages }</span>
                      </span>
                    ) }
                  </div>
                  <div className="d-flex flex-row py-4 align-items-center">
                    <Pagination totalRecords={totalCountries} pageLimit={18} pageNeighbours={1} onPageChanged={this.onPageChanged} />
                  </div>
                </div>
                { currentCountries.map(country => <CountryCard key={country.cca3} country={country} />) }
              </div>
            </div>
          );
        }
      }
      

      Dans la méthode render(), vous générez le nombre total de pays, la page en cours, le nombre total de pages, le contrôle <Pagination>, puis la <CountryCard> pour chaque pays de la page en cours.

      Vous remarquerez que vous avez passé la méthode onPageChanged() que vous avez définie précédemment au prop onPageChanged du contrôle de <Pagination>. Ceci est très important pour la saisie des changements de page à partir du composant Pagination. Vous affichez également 18 pays par page.

      À ce stade, l’application ressemblera à la capture d’écran suivante :

      Capture d'écran de l'application avec 248 pays listés et les numéros de page indiqués en haut pour parcourir chaque page

      Vous avez maintenant une App qui affiche plusieurs composants CountryCard et uncomposant Pagination qui décompose le contenu en pages séparées. Ensuite, vous étudierez le style de votre application.

      Étape 5 – Ajout de styles personnalisés

      Vous avez peut-être remarqué que vous avez ajouté des classes personnalisées aux composants que vous avez créés précédemment. Définissons quelques règles de style pour ces classes dans le fichier src/App.scss.

      Le fichierApp.scss ressemblera à l’extrait suivant :

      src/App.scss

      /* Declare some variables */
      $base-color: #ced4da;
      $light-background: lighten(desaturate($base-color, 50%), 12.5%);
      
      .current-page {
        font-size: 1.5rem;
        vertical-align: middle;
      }
      
      .country-card-container {
        height: 60px;
        cursor: pointer;
        position: relative;
        overflow: hidden;
      }
      
      .country-name {
        font-size: 0.9rem;
      }
      
      .country-region {
        font-size: 0.7rem;
      }
      
      .current-page,
      .country-name,
      .country-region {
        line-height: 1;
      }
      
      // Override some Bootstrap pagination styles
      ul.pagination {
        margin-top: 0;
        margin-bottom: 0;
        box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
      
        li.page-item.active {
          a.page-link {
            color: saturate(darken($base-color, 50%), 5%) !important;
            background-color: saturate(lighten($base-color, 7.5%), 2.5%) !important;
            border-color: $base-color !important;
          }
        }
      
        a.page-link {
          padding: 0.75rem 1rem;
          min-width: 3.5rem;
          text-align: center;
          box-shadow: none !important;
          border-color: $base-color !important;
          color: saturate(darken($base-color, 30%), 10%);
          font-weight: 900;
          font-size: 1rem;
      
          &:hover {
            background-color: $light-background;
          }
        }
      }
      

      Modifiez votre fichierApp.jspour référencer App.scss au lieu de App.css.

      Remarque : Pour plus d’informations à ce sujet, voir la documentation de l’application Create React.

      src/App.js

      import React, { Component } from 'react';
      import Countries from 'countries-api';
      import './App.scss';
      import Pagination from './components/Pagination';
      import CountryCard from './components/CountryCard';
      

      Après avoir ajouté les styles, l’application ressemblera maintenant à la capture d’écran suivante :

      Capture d'écran de l'app, page 1 sur 14, avec les styles

      Vous avez maintenant une application complète avec un style personnalisé supplémentaire. Vous pouvez utiliser des styles personnalisés pour modifier et améliorer les styles par défaut fournis par des bibliothèques comme Bootstrap.

      Conclusion

      Dans ce tutoriel, vous avez créé un widget de pagination personnalisé dans votre application React. Bien que vous n’ayez pas fait d’appel à une API ou interagi avec une base de données dans ce tutoriel, votre application peut exiger de telles interactions. Vous n’êtes en aucun cas limité à l’approche utilisée dans ce tutoriel – vous pouvez l’étendre comme vous le souhaitez pour répondre aux exigences de votre application.

      Pour obtenir le code source complet de ce tutoriel, consultez le dépôt build-react-pagination-demo sur GitHub. Vous pouvez également obtenir une démo live de ce tutoriel sur Code Sandbox.

      Si vous souhaitez en savoir plus sur React, consultez notre série Comment coder dans React.js ou consultez notre page thématique React pour des exercices et des projets de programmation.



      Source link

      How To Implement Pagination in MySQL with PHP on Ubuntu 18.04


      The author selected the the Apache Software Foundation to receive a donation as part of the Write for DOnations program.

      Introduction

      Pagination is the concept of constraining the number of returned rows in a recordset into separate, orderly pages to allow easy navigation between them, so when there is a large dataset you can configure your pagination to only return a specific number of rows on each page. For example, pagination can help to avoid overwhelming users when a web store contains thousands of products by reducing the number of items listed on a page, as it’s often unlikely a user will need to view every product. Another example is an application that shows records on a mobile device; enabling pagination in such a case would split records into multiple pages that can fit better on a screen.

      Besides the visual benefits for end-users, pagination makes applications faster because it reduces the number of records that are returned at a time. This limits the data that needs to be transmitted between the client and the server, which helps preserve server resources such as RAM.

      In this tutorial, you’ll build a PHP script to connect to your database and implement pagination to your script using the MySQL LIMIT clause.

      Prerequisites

      Before you begin, you will need the following:

      Step 1 — Creating a Database User and a Test Database

      In this tutorial you’ll create a PHP script that will connect to a MySQL database, fetch records, and display them in an HTML page within a table. You’ll test the PHP script in two different ways from your web browser. First, creating a script without any pagination code to see how the records are displayed. Second, adding page navigation code in the PHP file to understand how pagination works practically.

      The PHP code requires a MySQL user for authentication purposes and a sample database to connect to. In this step you’ll create a non-root user for your MySQL database, a sample database, and a table to test the PHP script.

      To begin log in to your server. Then log in to your MySQL server with the following command:

      Enter the root password of your MySQL server and hit ENTER to continue. Then, you’ll see the MySQL prompt. To create a sample database, which we will call test_db in this tutorial, run the following command:

      You will see the following output:

      Output

      Query OK, 1 row affected (0.00 sec)

      Then, create a test_user and grant the user all privileges to the test_db. Replace PASSWORD with a strong value:

      • GRANT ALL PRIVILEGES ON test_db.* TO 'test_user'@'localhost' IDENTIFIED BY 'PASSWORD';

      Output

      Query OK, 1 row affected (0.00 sec)

      Reload the MySQL privileges with:

      Output

      Query OK, 1 row affected (0.00 sec)

      Next, switch to the test_db database to start working directly on the test_db database:

      Output

      Database changed

      Now create a products table. The table will hold your sample products—for this tutorial you’ll require only two columns for the data. The product_id column will serve as the primary key to uniquely identify each record. You’ll use the product_name field to differentiate each item by name:

      • Create table products (product_id BIGINT PRIMARY KEY, product_name VARCHAR(50) NOT NULL ) Engine = InnoDB;

      Output

      Query OK, 0 rows affected (0.02 sec)

      To add ten test products to the products table run the following SQL statements:

      • Insert into products(product_id, product_name) values ('1', 'WIRELESS MOUSE');
      • Insert into products(product_id, product_name) values ('2', 'BLUETOOTH SPEAKER');
      • Insert into products(product_id, product_name) values ('3', 'GAMING KEYBOARD');
      • Insert into products(product_id, product_name) values ('4', '320GB FAST SSD');
      • Insert into products(product_id, product_name) values ('5', '17 INCHES TFT');
      • Insert into products(product_id, product_name) values ('6', 'SPECIAL HEADPHONES');
      • Insert into products(product_id, product_name) values ('7', 'HD GRAPHIC CARD');
      • Insert into products(product_id, product_name) values ('8', '80MM THERMAL PRINTER');
      • Insert into products(product_id, product_name) values ('9', 'HDMI TO VGA CONVERTER');
      • Insert into products(product_id, product_name) values ('10', 'FINGERPRINT SCANNER');

      You’ll see this output:

      Output

      Query OK, 1 row affected (0.02 sec)

      Verify that the products were inserted to the table by running:

      You’ll see the products in your output within the two columns:

      Output

      +------------+-----------------------+ | product_id | product_name | +------------+-----------------------+ | 1 | WIRELESS MOUSE | | 2 | BLUETOOTH SPEAKER | | 3 | GAMING KEYBOARD | | 4 | 320GB FAST SSD | | 5 | 17 INCHES TFT | | 6 | SPECIAL HEADPHONES | | 7 | HD GRAPHIC CARD | | 8 | 80MM THERMAL PRINTER | | 9 | HDMI TO VGA CONVERTER | | 10 | FINGERPRINT SCANNER | +------------+-----------------------+ 10 rows in set (0.00 sec)

      Exit MySQL:

      With the sample database, table, and test data in place, you can now create a PHP script to display data on a web page.

      Now you’ll create a PHP script that connects to the MySQL database that you created in the previous step and list the products in a web browser. In this step, your PHP code will run without any form of pagination to demonstrate how non-split records show on a single page. Although you only have ten records for testing purposes in this tutorial, seeing the records without pagination will demonstrate why segmenting data will ultimately create a better user experience and put less burden on the server.

      Create the PHP script file in the document root of your website with the following command:

      • sudo nano /var/www/html/pagination_test.php

      Then add the following content to the file. Remember to replace PASSWORD with the correct value of the password that you assigned to the test_user in the previous step:

      /var/www/html/pagination_test.php

      <?php
      
      try {
      
          $pdo = new PDO("mysql:host=localhost;dbname=test_db", "test_user", "PASSWORD");
          $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
          $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);
      
          $sql="select * from products";
      
          $stmt = $pdo->prepare($sql);
      
          $stmt->execute();
      
          echo "<table border='1' align='center'>";
      
          while ( ($row = $stmt->fetch(PDO::FETCH_ASSOC) ) !== false) {
              echo "<tr>";
      
              echo "<td>".$row['product_id']."</td>";
      
              echo "<td>".$row['product_name']."</td>";
      
              echo "</tr>";
      
          }
      
          echo "</table>";
      
      }
      
        catch(PDOException $e)
      
      {
          echo  $e->getMessage();
      }
      
      ?>
      

      Save the file by pressing CTRL+X, Y, and ENTER.

      In this script you’re connecting to the MySQL database using the PDO (PHP Data Object) library with the database credentials that you created in Step 1.

      PDO is a light-weight interface for connecting to databases. The data access layer is more portable and can work on different databases with just minor code rewrites. PDO has greater security since it supports prepared statements—a feature for making queries run faster in a secure way.

      Then, you instruct the PDO API to execute the select * from products statement and list products in an HTML table without pagination. The line $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false); ensures that the data types are returned as they appear in the database. This means that PDO will return the product_id as an integer and the product_name as a string. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); instructs PDO to throw an exception if an error is encountered. For easier debugging you’re catching the error inside the PHP try{}...catch{} block.

      To execute the /var/www/html/pagination_test.php PHP script file that you’ve created, visit the following URL replacing your-server-IP with the public IP address of your server:

      http://your-server-IP/pagination_test.php
      

      You’ll see a page with a table of your products.

      MySQL Records Displayed with a PHP script - No Pagination

      Your PHP script is working as expected; listing all products on one page. If you had thousands of products, this would result in a long loop as the products are fetched from the database and rendered on the PHP page.

      To overcome this limitation, you will modify the PHP script and include the MySQL LIMIT clause and some navigation links at the bottom of the table to add pagination functionality.

      In this step your goal is to split the test data into multiple and manageable pages. This will not only enhance readability but also use the resources of the server more efficiently. You will modify the PHP script that you created in the previous step to accommodate pagination.

      To do this, you’ll be implementing the MySQL LIMIT clause. Before adding this to the script, let’s see an example of the MySQL LIMIT syntax:

      • Select [column1, column2, column n...] from [table name] LIMIT offset, records;

      The LIMIT clause takes two arguments as shown toward the end of this statement. The offset value is the number of records to skip before the first row. records sets the maximum number of records to display per page.

      To test pagination, you’ll display three records per page. To get the total number of pages, you must divide the total records from your table with the rows that you want to display per page. You then round the resulting value to the nearest integer using PHP Ceil function as shown in the following PHP code snippet example:

      $total_pages=ceil($total_records/$per_page);
      

      Following is the modified version of the PHP script with the full pagination code. To include the pagination and navigation codes, open the /var/www/html/pagination_test.php file:

      • sudo nano /var/www/html/pagination_test.php

      Then, add the following highlighted code to your file:

      /var/www/html/pagination_test.php

      <?php
      
      try {
      
          $pdo = new PDO("mysql:host=localhost;dbname=test_db", "test_user", "PASSWORD");
          $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
          $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);
      
          /* Begin Paging Info */
      
          $page=1;
      
          if (isset($_GET['page'])) {
              $page=filter_var($_GET['page'], FILTER_SANITIZE_NUMBER_INT);
          }
      
          $per_page=3;
      
          $sqlcount="select count(*) as total_records from products";
          $stmt = $pdo->prepare($sqlcount);
          $stmt->execute();
          $row = $stmt->fetch();
          $total_records= $row['total_records'];
      
          $total_pages=ceil($total_records/$per_page);
      
          $offset=($page-1)*$per_page;
      
          /* End Paging Info */
      
          $sql="select * from products limit $offset,$per_page";
      
          $stmt = $pdo->prepare($sql);
      
          $stmt->execute();
      
      
          echo "<table border='1' align='center'>";
      
          while ( ($row = $stmt->fetch(PDO::FETCH_ASSOC) ) !== false) {
              echo "<tr>";
      
              echo "<td>".$row['product_id']."</td>";
      
              echo "<td>".$row['product_name']."</td>";
      
              echo "</tr>";
      
          }
      
          echo "</table>";
      
          /* Begin Navigation */
      
          echo "<table border='1' align='center'>";
      
          echo "<tr>";
      
          if( $page-1>=1) {
              echo "<td><a href=".$_SERVER['PHP_SELF']."?page=".($page-1).">Previous</a></td>";
          }
      
          if( $page+1<=$total_pages) {
              echo "<td><a href=".$_SERVER['PHP_SELF']."?page=".($page+1).">Next</a></td>";
          }
      
          echo "</tr>";
      
          echo "</table>";
      
          /* End Navigation */
      
      }
      
      catch(PDOException $e) {
              echo  $e->getMessage();
      }
      
      ?>
      

      In your file you’ve used additional parameters to execute paging:

      • $page : This variable holds the current page in your script. When moving between the pages, your script retrieves a URL parameter named page using the $_GET['page'] variable.
      • $per_page: This variable holds the maximum records that you want to be displayed per page. In your case, you want to list three products on each page.
      • $total_records: Before you list the products, you’re executing a SQL statement to get a total count of records in your target table and assigning it to the $total_records variable.
      • $offset: This variable represents the total records to skip before the first row. This value is calculated dynamically by your PHP script using the formula $offset=($page-1)*$per_page. You may adapt this formula to your PHP pagination projects. Remember you can change the $per_page variable to suit your needs. For instance, you might change it to a value of 50 to display fifty items per page if you’re running a website or another amount for a mobile device.

      Again, visit your IP address in a browser and replace your_server_ip with the public IP address of your server:

      http://your_server_ip/pagination_test.php
      

      You’ll now see some navigation buttons at the bottom of the page. On the first page, you will not get a Previous button. The same case happens on the last page where you will not get the Next page button. Also, note how the page URL parameter changes as you visit each page.

      MySQL Records Displayed with a PHP script with Pagination - Page 1

      MySQL Records Displayed with a PHP script with Pagination - Page 2

      Final page of MySQL Records Displayed with a PHP script with Pagination - Page 4

      The navigation links at the bottom of the page are achieved using the following PHP code snippet from your file:

      /var/www/html/pagination_test.php

      . . .
          if( $page-1>=1) {
              echo "<td><a href=".$_SERVER['PHP_SELF']."?page=".($page-1).">Previous</a></td>";
          }
      
          if( $page+1<=$total_pages) {
              echo "<td><a href=".$_SERVER['PHP_SELF']."?page=".($page+1).">Next</a></td>";
          }
      . . .
      

      Here, the $page variable represents the current page number. Then, to get the previous page, the code will minus 1 from the variable. So, if you’re on page 2, the formula (2-1) will give you a result of 1 and this will be the previous page to appear in the link. However, keep in mind that it will only show the previous page if there is a result greater or equal to 1.

      Similarly, to get to the next page, you add one to the $page variable and you must also make sure that the $page result that we append to the page URL parameter is not greater than the total pages that you’ve calculated in your PHP code.

      At this point, your PHP script is working with pagination and you are able to implement the MySQL LIMIT clause for better record navigation.

      Conclusion

      In this tutorial, you implemented paging in MySQL with PHP on an Ubuntu 18.04 server. You can use these steps with a larger recordset using the PHP script to include pagination. By using pagination on your website or application you can create better user navigation and optimum resource utilization on your server.

      From here you could consider further optimization for your database and other database tasks with these tutorials:



      Source link