A propos de l'auteur

Kristofer est le responsable de React chez Bekk et a travaillé sur de nombreux grands projets au cours des 6 dernières années. Il anime une rencontre de programmation basée sur la bière, essaie de garder son…
Plus à propos
Kristofer

Rendre vos tableaux triables dans React peut sembler une tâche intimidante, mais cela ne doit pas être trop difficile. Dans cet article, nous allons mettre en œuvre tout ce dont vous avez besoin pour trier tous vos besoins de tri de table.

Le tri des tableaux a toujours été un problème assez difficile à résoudre. Il existe de nombreuses interactions à suivre, de nombreuses mutations DOM à effectuer et même des algorithmes de tri complexes. Ce n’est qu’un de ces défis difficiles à relever. Droite?

Au lieu de tirer dans des bibliothèques externes, essayons de créer nous-mêmes des choses. Dans cet article, nous allons créer un moyen réutilisable de trier vos données tabulaires dans React. Nous allons parcourir chaque étape en détail et apprendre un tas de techniques utiles en cours de route.

Nous ne passerons pas par React ou la syntaxe JavaScript de base, mais vous n'avez pas besoin d'être un expert en React pour suivre.

Créer une table avec React

Commençons par créer un exemple de composant de table. Il accepte une gamme de produits et génère un tableau très basique, répertoriant une ligne par produit.

function ProductTable (accessoires) {
  const {produits} = accessoires;
  revenir (
    
        
        {products.map (product => (
          
        
        ))}
      
Nos produits
Nom Prix En stock
{product.name} {prix du produit} {product.stock}
); }

Ici, nous acceptons une gamme de produits et les bouclons dans notre table. C'est statique et non triable pour le moment, mais ça va pour le moment.

Tri des données

Si vous croyez tous les enquêteurs du tableau blanc, vous penseriez que le développement de logiciels était presque tous des algorithmes de tri. Heureusement, nous ne chercherons pas ici un tri rapide ou un tri à bulles.

Le tri des données en JavaScript est assez simple, grâce à la fonction de tableau intégrée Trier(). Il triera les tableaux de nombres et de chaînes sans argument supplémentaire:

const array = ['mozzarella', 'gouda', 'cheddar'];
array.sort ();
console.log (tableau); // ['cheddar', 'gouda', 'mozzarella']

Si vous voulez quelque chose d'un peu plus intelligent, vous pouvez lui passer une fonction de tri. Cette fonction se voit attribuer deux éléments dans la liste en tant qu'arguments et placera l'un devant l'autre en fonction de ce que vous déciderez.

Commençons par trier les données que nous obtenons par ordre alphabétique par nom.

function ProductTable (accessoires) {
  const {produits} = accessoires;
  laissez sortedProducts = [...products];
  sortedProducts.sort ((a, b) => {
    if (a.name < b.name) {
      return -1;
    }
    if (a.name > b.name) {
      retour 1;
    }
    retourner 0;
  });
  revenir (
    
      {/* comme avant */}
    
); }

Alors qu'est-ce qui se passe ici? Tout d'abord, nous créons une copie de l'accessoire des produits, que nous pouvons modifier et changer à notre guise. Nous devons le faire parce que le Array.prototype.sort La fonction modifie le tableau d'origine au lieu de renvoyer une nouvelle copie triée.

Ensuite, nous appelons sortedProducts.sortet passez-le tri une fonction. Nous vérifions si le Nom propriété du premier argument une est avant le deuxième argument b, et si oui, renvoyez une valeur négative. Cela indique que une devrait venir avant b dans la liste. Si le nom du premier argument est après le nom du deuxième argument, nous renvoyons un nombre positif, indiquant que nous devons placer b avant une. Si les deux sont égaux (c'est-à-dire que les deux ont le même nom), nous retournons 0 pour conserver l'ordre.

Rendre notre table triable

Alors maintenant, nous pouvons nous assurer que la table est triée par nom – mais comment pouvons-nous changer l'ordre de tri nous-mêmes?

Pour modifier le champ par lequel nous trions, nous devons nous souvenir du champ actuellement trié. Nous le ferons avec le useState crochet.

Un crochet est un type spécial de fonction qui nous permet de «nous accrocher» à certaines des fonctionnalités de base de React, comme la gestion de l'état et le déclenchement d'effets secondaires. Ce crochet particulier nous permet de maintenir un morceau d'état interne dans notre composant et de le changer si nous le voulons. Voici ce que nous ajouterons:

const [sortedField, setSortedField] = React.useState (null);

Nous commençons par ne rien trier du tout. Ensuite, modifions les en-têtes de tableau pour inclure un moyen de changer le champ selon lequel nous voulons trier.

const ProductsTable = (props) => {
  const {produits} = accessoires;
  const [sortedField, setSortedField] = React.useState (null);
  revenir (
    
      
      {/* Comme avant */}
    
); };

Maintenant, chaque fois que nous cliquons sur un en-tête de tableau, nous mettons à jour le champ selon lequel nous voulons trier. Neat-o!

Nous n'effectuons pas encore de tri réel, alors corrigeons cela. Rappelez-vous l'algorithme de tri d'avant? Le voici, juste légèrement modifié pour fonctionner avec n'importe lequel de nos noms de champs.

const ProductsTable = (props) => {
  const {produits} = accessoires;
  const [sortedField, setSortedField] = React.useState (null);
  laissez sortedProducts = [...products];
  if (sortedField! == null) {
    sortedProducts.sort ((a, b) => {
      si un[sortedField] < b[sortedField]) {
        return -1;
      }
      if (a[sortedField] >  b[sortedField]) {
        retour 1;
      }
      retourner 0;
    });
  }
  revenir (
    

Nous nous assurons d'abord que nous avons choisi un champ pour trier, et si oui, nous trions les produits par ce champ.

Croissant vs Décroissant

La prochaine fonctionnalité que nous voulons voir est un moyen de basculer entre l'ordre croissant et décroissant. Nous basculerons entre l'ordre croissant et décroissant en cliquant une nouvelle fois sur l'en-tête du tableau.

Pour implémenter cela, nous devons introduire un deuxième état – l'ordre de tri. Nous allons refactoriser notre courant sortedField variable d'état pour conserver à la fois le nom du champ et sa direction. Au lieu de contenir une chaîne, cette variable d'état contiendra un objet avec une clé (le nom du champ) et une direction. Nous le renommerons sortConfig pour être un peu plus clair.

Voici la nouvelle fonction de tri:

  sortedProducts.sort ((a, b) => {
  si un[sortConfig.key] < b[sortConfig.key]) {
    return sortConfig.direction === 'ascending' ? -1 : 1;
  }
  if (a[sortConfig.key] >  b[sortConfig.key]) {
    retourner sortConfig.direction === 'croissant'? 1: -1;
  }
  retourner 0;
});

Maintenant, si la direction est "ascendante", nous ferons comme nous l'avons fait précédemment. Si ce n'est pas le cas, nous ferons le contraire en nous donnant un ordre décroissant.

Ensuite, nous allons créer une nouvelle fonction – requestSort – qui acceptera le nom du champ et mettra à jour l'état en conséquence.

const requestSort = key => {
  let direction = 'ascendant';
  if (sortConfig.key === key && sortConfig.direction === 'ascendant') {
    direction = 'descendant';
  }
  setSortConfig ({clé, direction});
}

Nous devrons également modifier nos gestionnaires de clics pour utiliser cette nouvelle fonction!

revenir (
  
{/* comme avant */}
);

Maintenant, nous commençons à avoir l'air assez complet, mais il reste encore une grande chose à faire. Nous devons nous assurer de ne trier nos données que lorsque nous en avons besoin. Actuellement, nous trions toutes nos données sur chaque rendu, ce qui entraînera toutes sortes de problèmes de performances sur toute la ligne. Au lieu de cela, utilisons la fonction intégrée useMemo crochet pour mémoriser toutes les parties lentes!

const ProductsTable = (props) => {
  const {produits} = accessoires;
  const [sortConfig, setSortConfig] = React.useState (null);
  
  React.useMemo (() => {
    laissez sortedProducts = [...products];
    if (sortedField! == null) {
      sortedProducts.sort ((a, b) => {
        si un[sortConfig.key] < b[sortConfig.key]) {
          return sortConfig.direction === 'ascending' ? -1 : 1;
        }
        if (a[sortConfig.key] >  b[sortConfig.key]) {
          retourner sortConfig.direction === 'croissant'? 1: -1;
        }
        retourner 0;
      });
    }
    return sortedProducts;
  }, [products, sortConfig]);

Si vous ne l'avez jamais vu auparavant, useMemo est un moyen de mettre en cache – ou de mémoriser – des calculs coûteux. Donc, étant donné la même entrée, il n'est pas nécessaire de trier les produits deux fois si nous restituons notre composant pour une raison quelconque. Notez que nous voulons déclencher un nouveau tri chaque fois que nos produits changent, ou le champ ou la direction que nous trions par changements.

Envelopper notre code dans cette fonction aura d'énormes implications en termes de performances pour notre tri de table!

Rendre tout réutilisable

L'une des meilleures choses à propos des crochets est la facilité avec laquelle il est possible de rendre la logique réutilisable. Vous allez probablement trier tous les types de tableaux dans votre application, et devoir réimplémenter à nouveau les mêmes éléments ressemble à un glisser-déposer.

React a cette fonctionnalité appelée crochets personnalisés. Ils semblent fantaisistes, mais ce ne sont que des fonctions régulières qui utilisent d'autres crochets à l'intérieur d'eux. Refactorisons notre code pour qu'il soit contenu dans un crochet personnalisé, afin que nous puissions l'utiliser partout!

const useSortableData = (items, config = null) => {
  const [sortConfig, setSortConfig] = React.useState (config);
  
  const sortedItems = React.useMemo (() => {
    laissez sortableItems = [...items];
    if (sortConfig! == null) {
      sortableItems.sort ((a, b) => {
        si un[sortConfig.key] < b[sortConfig.key]) {
          return sortConfig.direction === 'ascending' ? -1 : 1;
        }
        if (a[sortConfig.key] >  b[sortConfig.key]) {
          retourner sortConfig.direction === 'croissant'? 1: -1;
        }
        retourner 0;
      });
    }
    return sortableItems;
  }, [items, sortConfig]);

  const requestSort = key => {
    let direction = 'ascendant';
    if (sortConfig && sortConfig.key === clé && sortConfig.direction === 'ascendant') {
      direction = 'descendant';
    }
    setSortConfig ({clé, direction});
  }

  renvoyer {items, requestSort};
}

Il s'agit à peu près de copier-coller de notre code précédent, avec un peu de changement de nom. useSortableData accepte les éléments et un état de tri initial facultatif. Il renvoie un objet avec les éléments triés et une fonction pour trier à nouveau les éléments.

Notre code de table ressemble maintenant à ceci:

const ProductsTable = (props) => {
  const {produits} = accessoires;
  const {items, requestSort} = useSortableData (produits);
  revenir (
    {/ * ... * /}
); };

Une dernière touche

Il manque un tout petit morceau – un moyen d’indiquer comment la table est triée. Afin d'indiquer que dans notre conception, nous devons également retourner l'état interne – le sortConfig. Revenons-en aussi et utilisons-le pour générer des styles que nous pouvons appliquer à nos en-têtes de tableau!

const ProductTable = (props) => {
  const {items, requestSort, sortConfig} = useSortableData (props.products);
  const getClassNamesFor = (nom) => {
    if (! sortConfig) {
      revenir;
    }
    retourner sortConfig.key === nom? sortConfig.direction: undefined;
  };
  revenir (
    
         
         {/ *… * /}
        
      
      {/ *… * /}
    
Des produits
); };

Et avec ça, c'est fini!

Emballer

En fin de compte, créer votre propre algorithme de tri de table n'était pas un exploit impossible après tout. Nous avons trouvé un moyen de modéliser notre état, nous avons écrit une fonction de tri générique et nous avons écrit un moyen de mettre à jour nos préférences de tri. Nous nous sommes assurés que tout était performant et avons refactorisé le tout dans un crochet personnalisé. Enfin, nous avons fourni un moyen d'indiquer l'ordre de tri à l'utilisateur.

Vous pouvez voir une démonstration du tableau dans ce CodeSandbox:

Smashing Editorial(ra, yk, il)