Contactez-nous

Pièges courants avec les clés (index comme clé)

Découvrez les pièges courants liés à l'utilisation de l'index de tableau comme prop `key` en React. Comprenez pourquoi cela peut causer des bugs, affecter les performances et l'état.

La facilité trompeuse de l'index du tableau

Lorsqu'on génère une liste avec `map()`, l'index de chaque élément dans le tableau (`map((item, index) => ...`) semble être un candidat évident et facile pour la prop `key`. Après tout, il est unique pour chaque élément *à un instant T* et il est fourni gratuitement par la méthode `map()`. Cependant, cette facilité cache des pièges potentiels qui peuvent entraîner des bugs subtils et des problèmes de performance, surtout lorsque la liste est dynamique (c'est-à-dire susceptible de changer).

Le problème fondamental réside dans le manque de **stabilité** de l'index. L'index d'un élément n'est pas lié à l'identité intrinsèque de la donnée qu'il représente, mais uniquement à sa *position* dans le tableau à un moment donné. Si la position change (parce que la liste est réordonnée, filtrée, ou que des éléments sont ajoutés/supprimés), l'index change aussi, ce qui trompe React lors de la réconciliation.

Piège n°1 : Problèmes lors du réordonnancement

C'est l'un des scénarios les plus classiques où l'utilisation de l'index comme clé pose problème. Imaginons une liste d'éléments où chaque élément contient un champ de saisie (``). Si l'utilisateur saisit du texte dans un champ, puis réordonne la liste (par exemple, en cliquant sur un bouton "Monter" ou "Descendre"), React va essayer de réconcilier l'ancienne liste avec la nouvelle.

Si les clés sont les index, React verra que les éléments aux index 0, 1, 2, etc., existent toujours. Il ne déplacera pas les éléments dans le DOM. Au lieu de cela, il réutilisera les instances de composants existantes à chaque position et mettra simplement à jour leurs props (par exemple, le texte affiché à côté de l'input). Conséquence : l'état local de l'input (le texte saisi par l'utilisateur) restera attaché à la *position* (l'index), et non à l'élément de donnée logique qui a été déplacé ! Le texte saisi semblera 'sauter' à un autre élément.

function UnstableList() {
  const [items, setItems] = React.useState([
    { id: 'a', text: 'Item A' }, 
    { id: 'b', text: 'Item B' }
  ]);

  const reverseList = () => {
    setItems([...items].reverse());
  };

  return (
    
    {items.map((item, index) => ( // !! Utilisation de l'index comme clé !!
  • {item.text} -
  • ))}
); } // Si vous tapez 'Test A' dans le premier input, puis cliquez sur 'Inverser', // 'Test A' restera dans le premier input, maintenant associé à 'Item B'. // Avec une clé stable (key={item.id}), l'input et son contenu suivraient 'Item A'.

Piège n°2 : Problèmes lors de l'insertion ou de la suppression

Un problème similaire survient lors de l'ajout ou de la suppression d'éléments, en particulier au début ou au milieu de la liste. Supposons que vous ajoutiez un nouvel élément au début d'une liste utilisant les index comme clés.

Tous les éléments existants se décalent et obtiennent un nouvel index (`index + 1`). Pour React, cela ressemble à une mise à jour de *tous* les éléments existants (car leurs props pourraient changer si elles dépendent de l'item) et à l'ajout d'un nouvel élément à la fin. Si les composants ont un état local ou effectuent des opérations coûteuses lors du rendu, cela peut être très inefficace.

Pire encore, si vous supprimez un élément au milieu, les éléments suivants auront également de nouveaux index, pouvant entraîner les mêmes problèmes de désynchronisation d'état que lors du réordonnancement.

function ItemWithState({ id }) {
  const [count, setCount] = React.useState(0);
  console.log(`Rendu Item ${id}`);
  return (
    
  • Item ID: {id} - Count: {count}
  • ); } function AnotherUnstableList() { const [items, setItems] = React.useState([{id: 'a'}, {id: 'b'}]); const addItemToStart = () => { const newItem = { id: Date.now().toString() }; // Nouvel ID unique setItems([newItem, ...items]); }; return (
      {items.map((item, index) => ( // !! Utilisation de l'index comme clé !! ))}
    ); } // Si vous incrémentez le compteur du premier item (id: 'a'), puis ajoutez un nouvel item, // l'ancien premier item (id: 'a') prendra l'index 1. L'instance de composant à l'index 0 // sera réutilisée pour le nouvel item (nouvel id), mais elle héritera du compteur de l'ancien item 'a' ! // L'instance à l'index 1 (anciennement index 0) sera mise à jour avec l'id 'a' mais gardera son état (le compteur incrémenté). // Avec key={item.id}, chaque ItemWithState garderait son propre compteur associé à son id.

    Conclusion : privilégier les identifiants stables

    L'utilisation de l'index comme clé est une optimisation prématurée qui cause souvent plus de problèmes qu'elle n'en résout. Elle viole le principe de stabilité nécessaire à React pour une réconciliation efficace et correcte.

    A moins que vous ne soyez absolument certain que votre liste est parfaitement statique (jamais réordonnée, filtrée, ni modifiée par insertion/suppression autre qu'à la fin) ET que vos éléments n'ont aucun état local pertinent, évitez d'utiliser l'index comme clé. Prenez le temps d'identifier ou de créer un identifiant unique et stable lié à vos données (`item.id`). C'est la garantie d'un comportement prévisible, de performances optimales et d'une maintenance facilitée pour vos listes dynamiques en React.