Fermer

novembre 27, 2023

Comment créer une table triable et filtrable dans React —

Comment créer une table triable et filtrable dans React —


Les tableaux dynamiques sont souvent utilisés dans les applications Web pour représenter les données dans un format structuré. Le tri et le filtrage de l’ensemble de données peuvent accélérer les processus lorsque vous travaillez avec de grands ensembles de données. Dans ce didacticiel, nous verrons comment créer un composant de table triable et filtrable dans React.

Vous pouvez trouver le code source complet en un seul morceau hébergé sur GitHub. Le résultat final est illustré ci-dessous.

Composant Table Finale

Table des matières

Conditions préalables

Avant de commencer, ce didacticiel suppose que vous possédez une connaissance de base de HTML, CSS, Javascriptet Réagir. Pendant que nous examinons le projet étape par étape, nous n’expliquerons pas en détail les concepts de base des méthodes de tableau React ou JavaScript. Nous utiliserons également Manuscrit, mais la même chose peut être obtenue sans cela. Cela étant dit, passons au codage.

Mise en place du projet

Pour ce projet, nous utiliserons Vite, un outil frontal robuste et populaire. Si vous ne disposez pas déjà d’une application React existante, vous pouvez démarrer un nouveau projet dans Vite à l’aide de l’une des commandes suivantes dans votre terminal :


npm create vite@latest folder-name -- --template react-ts


yarn create vite folder-name --template react-ts


pnpm create vite folder-name --template react-ts


bunx create-vite folder-name --template react-ts

Une fois que vous êtes prêt, créez un nouveau dossier pour le Table composant au sein du projet React avec la structure suivante :

src
├─ components
│  ├─ Table
│  │  ├─ index.ts 
│  │  ├─ table.css
│  │  ├─ Table.tsx
├─ App.tsx
  • index.ts. Nous utiliserons ce fichier pour réexporter Table.tsx pour simplifier les chemins d’importation.
  • table.css. Contient les styles associés au composant. Pour ce tutoriel, nous utiliserons le CSS Vanilla.
  • Table.tsx. Le composant lui-même.

Ouvrir Table.tsx et exportez ce qui suit, afin que nous puissions vérifier les charges du composant lorsque nous l’importons :

import './table.css'

export const Table = () => {
  return (
    <h1>Table component</h1>
  )
}

À l’intérieur index.tsréexportez le composant en utilisant la ligne suivante :

export * from './Table'

Maintenant que les fichiers de composants sont configurés, vérifions qu’ils se chargent en les important dans notre application. Dans ce tutoriel, nous utiliserons le App composant. Si vous avez un projet React existant, vous pouvez l’importer à l’emplacement souhaité. Importer le Table composant dans votre application comme ceci :

import { Table } from './components/Table'

const App = () => {
  return (
    <Table />
  )
}

export default App

Générer les données fictives

Bien sûr, pour travailler sur la table, nous aurons d’abord besoin de données fictives. Pour ce tutoriel, nous pouvons utiliser Générateur JSON, un service gratuit pour générer des données JSON aléatoires. Nous utiliserons le schéma suivant pour générer les données :

[
  '{{repeat(10)}}',
  {
    id: '{{index()}}',
    name: '{{firstName()}} {{surname()}}',
    company: '{{company().toUpperCase()}}',
    active: '{{bool()}}',
    country: '{{country()}}'
  }
]

JSON Generator est livré avec diverses fonctionnalités intégrées pour générer différents types de données. Le schéma ci-dessus créera un tableau d’objets avec dix objets aléatoires sous la forme de :

{
  id: 0,                 
  name: 'Jaime Wallace', 
  company: 'UBERLUX',    
  active: false,         
  country: 'Peru'        
}

Générez une liste d’entrées en utilisant le schéma ci-dessus, puis créez un nouveau fichier dans le src dossier appelé données.ts et exportez le tableau de la manière suivante :

export const data = [
  {
    id: 0,
    name: 'Jaime Wallace',
    company: 'UBERLUX',
    active: false,
    country: 'Peru'
  },
  { ... },
]

Ouvrir App.tsxet transmettez ces données au Table composant comme accessoire appelé rows. Nous allons générer le tableau basé sur ces données :

  import { Table } from './components/Table'
+ import { data } from './data'

  const App = () => {
    return (
-     <Table />
+     <Table rows={data} />
    )
  }

  export default App

Création du composant

Maintenant que nous avons configuré le composant et les données, nous pouvons commencer à travailler sur la table. Pour générer dynamiquement la table en fonction des données transmises, remplacez tout ce qui se trouve dans le Table composant avec les lignes de code suivantes :

import { useState } from 'react'

import './table.css'

export const Table = ({ rows }) => {
  const [sortedRows, setRows] = useState(rows)

  return (
    <table>
      <thead>
        <tr>
          {Object.keys(rows[0]).map((entry, index) => (
            <th key={index}>{entry}</th>
          ))}
        </tr>
      </thead>
      <tbody>
        {sortedRows.map((row, index) => (
          <tr key={index}>
            {Object.values(row).map((entry, columnIndex) => (
              <td key={columnIndex}>{entry}</td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  )
}

Cela générera dynamiquement à la fois les en-têtes et les cellules du tableau en fonction du rows soutenir. Décrivons comment cela fonctionne. Comme nous allons trier et filtrer les lignes, nous devons les stocker dans un état en utilisant le useState crochet. L’accessoire est transmis comme valeur initiale au hook.

Pour afficher les titres du tableau, nous pouvons utiliser Object.keys sur la première entrée du tableau, qui renverra les clés de l’objet sous forme de liste de chaînes :

const rows = [
  {
    id: 0,
    name: 'Jaime Wallace'
  },
  { ... }
]


Object.keys(rows[0]) -> ['id', 'name']


['id', 'name'].map((entry, index) => (...))

Pour afficher les cellules du tableau, nous devons utiliser Object.values sur chaque ligne, qui renvoie la valeur de chaque clé d’un objet, par opposition à Object.keys. En détail, voici comment nous affichons les cellules du tableau :

const sortedRows = [
  {
    id: 0,
    name: 'Jaime Wallace'
  },
  { ... }
]


{sortedRows.map((row, index) => (<tr key={index}>...</tr>))}


Object.values(row) -> [0, 'Jaime Wallace']

Cette approche rend extrêmement flexible l’utilisation de tout type de données avec notre Table composant, sans avoir à réécrire la logique. Jusqu’à présent, nous aurons créé le tableau suivant à l’aide de notre composant. Cependant, il y a quelques problèmes avec le formatage.

Problème de formatage avec le composant Table

Formatage des cellules du tableau

À l’heure actuelle, le active la colonne n’est pas affichée. En effet, les valeurs de ces champs sont booléennes et ne sont pas imprimées sous forme de chaînes dans JSX. Pour résoudre ce problème, nous pouvons introduire une nouvelle fonction permettant de formater les entrées en fonction de leurs valeurs. Ajoutez ce qui suit au Table composant et enveloppe entry dans la fonction dans le JSX :

const formatEntry = (entry: string | number | boolean) => {
  if (typeof entry === 'boolean') {
    return entry ? '✅' : '❌'
  }

  return entry
}

return (
  <table>
    <thead>...</thead>
    <tbody>
      {sortedRows.map((row, index) => (
        <tr key={index}>
          {Object.values(row).map((entry, columnIndex) => (
            <td key={columnIndex}>{formatEntry(entry)}</td>
          ))}
        </tr>
      ))}
    </tbody>
  </table>
)

Le formatEntry la fonction attend une entrée, qui dans notre cas peut être soit string, numberou booleanpuis renvoie une valeur formatée si le typeof entry est un booleanc’est-à-dire pour true valeurs, nous afficherons une coche verte, et pour false valeurs, nous afficherons une croix rouge. En utilisant une approche similaire, nous pouvons également formater les en-têtes des tableaux. Mettons-les en majuscule avec la fonction suivante :

export const capitalize = (
  str: string
) => str?.replace(/\b\w/g, substr => substr.toUpperCase())

Cette fonction utilise une expression régulière pour récupérer la première lettre de chaque mot et la transformer en majuscule. Pour utiliser cette fonction, nous pouvons créer un utils.ts fichier à la racine du src dossier, exportez cette fonction, puis importez-la dans notre Table composant à utiliser de la manière suivante :

import { capitalize } from '../../utils'

export const Table = ({ rows }) => {
  ...

  return (
      <table>
        <thead>
          <tr>
            {Object.keys(rows[0]).map((entry, index) => (
              <th key={index}>{capitalize(entry)}</th>
            ))}
          </tr>
        </thead>
        <tbody>...</tbody>
      </table>
  )
}

Sur la base de ces modifications, nous disposons désormais d’un tableau formaté et construit dynamiquement.

Tableau formaté dans React

Accessoires de frappe

Avant de commencer à styliser le tableau puis à ajouter des contrôles, tapons correctement le rows soutenir. Pour cela, nous pouvons créer un types.ts fichier à la racine du src dossier et exportez des types personnalisés qui peuvent être réutilisés tout au long du projet. Créez le fichier et exportez le type suivant :

export type Data = {
    id: number
    name: string
    company: string
    active: boolean
    country: string
}[]

Pour taper le rows accessoire dans le Table composant, importez simplement ce type et transmettez-le au composant de la manière suivante :

import { Data } from '../../types'

export type TableProps = {
  rows: Data
}

export const Table = ({ rows }: TableProps) => { ... }

Styliser la table

Pour styliser l’ensemble du composant table, nous n’aurons besoin que de quelques règles. Tout d’abord, nous souhaitons définir les couleurs et les bordures, ce que nous pouvons faire en utilisant les styles suivants :

table {
  width: 100%;
  border-collapse: collapse;
}

thead {
  text-align: left; 
  color: #939393;
  background: #2f2f2f;
}

th,td {
  padding: 4px 6px;
  border: 1px solid #505050;
}

Ajoutez ce qui précède à table.css. Assurez-vous de définir border-collapse à collapse sur le <table> pour éviter les doubles frontières. Comme le tableau s’étend sur tout l’écran, effectuons également quelques ajustements et supprimons les bordures gauche et droite, car elles ne sont de toute façon pas visibles :

th:first-child,
td:first-child {
  border-left: 0;
}

th:last-child,
th:last-child {
  border-right: 0;
}

Cela supprimera les frontières de chaque côté du <table>, ce qui donne un aspect plus propre. Enfin, ajoutons un effet de survol aux lignes du tableau pour aider visuellement les utilisateurs lors de leurs recherches dans le tableau :

tr:hover {
  background: #2f2f2f;
}

Avec tout jusqu’à présent, nous avons maintenant le comportement suivant pour le composant.

Effet de survol du tableau

Ajout de contrôles

Maintenant que nous avons stylisé le tableau, ajoutons les contrôles pour les fonctionnalités de tri et de filtrage. Nous allons créer un <input> pour le filtre et un <select> élément pour le tri. Nous inclurons également un bouton pour basculer entre les ordres de tri (ascendant/décroissant).

Tableau avec options de filtre

Pour ajouter les entrées, nous aurons également besoin de nouveaux états pour l’ordre actuel (croissant ou décroissant) et d’une variable pour garder une trace de la clé de tri (quelle clé de l’objet est utilisée pour le tri). Dans cette optique, étendre le Table composant avec les éléments suivants :

const [order, setOrder] = useState('asc')
const [sortKey, setSortKey] = useState(Object.keys(rows[0])[0])

const filter = (event: React.ChangeEvent<HTMLInputElement>) => {}
const sort = (value: keyof Data[0], order: string) => {}
const updateOrder = () => {}

return (
  <>
    <div className="controls">
      <input
        type="text"
        placeholder="Filter items"
        onChange={filter}
      />
      <select onChange={(event) => sort()}>
        {Object.keys(rows[0]).map((entry, index) => (
          <option value={entry} key={index}>
            Order by {capitalize(entry)}
          </option>
        ))}
      </select>
      <button onClick={updateOrder}>Switch order ({order})</button>
    </div>
    <table>...</table>
  </>
)

Allons-y afin de comprendre ce qui a changé :

  • order. Tout d’abord, nous devons créer un nouvel état pour l’ordre de tri. Cela peut être l’un des asc ou desc. Nous utiliserons sa valeur dans le sort fonction.
  • sortKey. Nous avons également besoin d’un état pour la clé de tri. Par défaut, nous pouvons récupérer la clé de la toute première propriété de notre tableau d’objets en utilisant Object.keys(rows[0])[0]. Nous l’utiliserons pour suivre le tri lors du passage d’une commande à l’autre.
  • filter. Nous aurons besoin d’une fonction pour filtrer les résultats. Cela doit être transmis au onChange événement sur le <input> élément. Noter que React.ChangeEvent est un générique et peut accepter le type d’élément HTML qui a déclenché le changement.
  • sort. Tout comme le filter fonction, celui-ci devra être attaché au onChange événement, mais cette fois, sur le <select> élément. Il acceptera deux paramètres :
  • value. Il peut prendre les clés de notre objet de données. Nous pouvons spécifier le type en utilisant le keyof mot-clé. Cela signifie que value peut être l’un des id, name, company, activeou country.
  • order. L’ordre du tri, soit asc ou desc.
  • updateOrder. Enfin, nous avons également besoin d’une fonction de mise à jour de la commande. Cela sera déclenché en cliquant sur le bouton.
  • Notez que nous utilisons la même logique que pour le <th> éléments pour générer dynamiquement les options pour le <select>. Nous pouvons également réutiliser le capitalize fonction utilitaire pour formater les options.

    Options de sélection disponibles

    Commandes de style

    Stylisons les contrôles avant d’aller de l’avant. Cela peut être fait avec seulement une poignée de règles CSS. Étendre table.css avec ce qui suit :

    .controls {
      display: flex;
    }
    
    input,
    select {
      flex: 1;
      padding: 5px 10px;
      border: 0;
    }
    
    button {
      background: #2f2f2f;
      color: #FFF;
      border: 0;
      cursor: pointer;
      padding: 5px 10px;
    }
    

    Cela garantira que les entrées sont alignées les unes à côté des autres. En utilisant flex: 1 sur le <input> et <select> éléments, nous pouvons leur faire occuper une largeur égale par rapport à l’espace disponible. Le <button> prendra autant de place que nécessaire pour son texte.

    Filtrage du tableau

    Maintenant que nous avons les contrôles en place, examinons la mise en œuvre de la fonctionnalité. Pour filtrer le tableau en fonction de n’importe quel champ, nous devrons suivre cette logique :

    const rows = [
      {
        id: 0,
        name: 'Jaime Wallace'
      },
      { ... }
    ]
    
    
    
    setRows([ ...rows ].filter(row => { ... }))
    
    
    
    Object.values(row) -> [0, 'Jaime Wallace']
    
    
    [0, 'Jaime Wallace'].join('') -> '0Jaime Wallace'
    
    
    '0Jaime Wallace'.toLowerCase() -> '0jaime wallace'
    
    
    '0jaime wallace'.includes(value) -> true / false
    

    Avec tout combiné, nous pouvons créer le return valeur pour le filter basé sur la logique ci-dessus. Cela nous laisse avec l’implémentation suivante pour le filter fonction:

    const filter = (event: React.ChangeEvent<HTMLInputElement>) => {
      const value = event.target.value
    
      if (value) {
        setRows([ ...rows.filter(row => {
          return Object.values(row)
            .join('')
            .toLowerCase()
            .includes(value)
        }) ])
      } else {
        setRows(rows)
      }
    }
    

    Notez que nous voulons également vérifier si le value est présent. Son absence signifie le <input> le champ est vide. Dans de tels cas, nous souhaitons réinitialiser l’état et transmettre le message non filtré rows à setRows pour réinitialiser la table.

    Filtrage du tableau

    Trier le tableau

    Nous avons la fonctionnalité de filtre, mais il nous manque encore le tri. Pour le tri, nous avons deux fonctions distinctes :

    • sort. La fonction qui gérera le tri.
    • updateOder. La fonction qui fera passer l’ordre de tri de croissant à décroissant et vice versa.

    Commençons par la fonction de tri. Chaque fois que le <select> changements, le sort la fonction sera appelée. Nous voulons utiliser la valeur de <select> élément pour décider quelle clé utiliser pour le tri. Pour cela, nous pouvons utiliser un simple sort méthode et notation entre crochets pour comparer dynamiquement les clés d’objet :

    const sort = (value: keyof Data[0], order: string) => {
      const returnValue = order === 'desc' ? 1 : -1
    
      setSortKey(value)
      setRows([ ...sortedRows.sort((a, b) => {
        return a[value] > b[value]
          ? returnValue * -1
          : returnValue
      }) ])
    }
    

    Passons en revue la fonction de haut en bas pour mieux comprendre l’implémentation.

    • returnValue. Basé sur order état, nous voulons que la valeur de retour soit 1 ou -1. Cela nous aide à définir l’ordre de tri (1 pour décroissant et -1 pour ascendant).
    • setSortKey. Le value transmise à la fonction est la valeur du <select> élément. Nous voulons enregistrer cette valeur dans notre état (sortKey), ce que nous pouvons faire en appelant le setSortKey fonction de mise à jour.
    • setRows. Le tri réel a lieu dans cet appel. En utilisant la notation entre parenthèses, nous pouvons comparer a[value] avec b[value] et renvoie soit -1 ou 1.

    Prenons comme exemple ce qui suit :

    const rows = [{ id: 0 }, { id: 1 }]
    const value = 'id'
    
    
    rows.sort((a, b) => a[value] > b[value] ? -1 : 1)
    
    
    rows.sort((a, b) => a[value] > b[value] ? 1 : -1)
    

    Basculer entre les ordres de tri

    Pour mettre à jour l’ordre de tri, il suffit de mettre à jour le order état à chaque fois que le bouton est cliqué. Nous pouvons y parvenir avec les fonctionnalités suivantes :

    const updateOrder = () => {
      const updatedOrder = order === 'asc' ? 'desc' : 'asc'
    
      setOrder(updatedOrder)
      sort(sortKey as keyof Data[0], updatedOrder)
    }
    

    Cela définira le order à son opposé à chaque clic. Notez qu’après avoir mis à jour le order état en utilisant setOrdernous devons également appeler le sort fonction pour recourir à la table en fonction de la commande mise à jour. Pour déduire le type correct pour le sortKey variable, nous pouvons référencer les clés de la Data tapez en utilisant le transtypage : as keyof Data[0]. En tant que deuxième paramètre, nous devons également transmettre la commande mise à jour.

    Commander la table

    Gestion du surfiltrage

    Pour terminer ce projet, ajoutons quelques indications pour un état surfiltré. Nous souhaitons uniquement afficher un état surfiltré s’il n’y a aucun résultat. Cela peut être facilement fait en vérifiant le length de nôtre sortedRows État. Après le <table> élément, ajoutez ce qui suit :

    return (
      <>
        <div className="controls">...</div>
        <table>...</table>
        {!sortedRows.length && (
          <h1>No results... Try expanding the search</h1>
        )}
      </>
    )
    

    État surfiltré

    Conclusion

    En conclusion, créer une table triable et filtrable dans React ne doit pas être compliqué. Avec les méthodes de tableau et le chaînage de fonctions, en utilisant les bonnes fonctionnalités, nous pouvons créer des fonctions concises et précises pour gérer ces tâches. Avec tout inclus, nous avons réussi à intégrer toute la logique dans moins de 100 lignes de code.

    Comme vu au début de ce tutoriel, l’ensemble du projet est disponible en un seul morceau sur GitHub. Merci d’avoir lu jusqu’au bout ; bon codage !




    Source link

    novembre 27, 2023