Listes et Keys
Afficher des listes est une tâche quotidienne en React. Apprenons à le faire correctement avec .map() et les keys.
Afficher des listes avec .map()
En React, on utilise la méthode .map() pour transformer un tableau en éléments JSX.
Exemple simple
function UserList() {
const users = ['Alice', 'Bob', 'Charlie'];
return (
<ul>
{users.map(user => (
<li>{user}</li>
))}
</ul>
);
}
Résultat :
• Alice
• Bob
• Charlie
Warning dans la console
Le code ci-dessus fonctionne mais génère un warning :
Warning: Each child in a list should have a unique "key" prop.
Pour résoudre cela, ajoutez une prop key unique à chaque élément.
La prop key - Pourquoi est-elle obligatoire ?
Les keys aident React à identifier quels éléments ont changé, été ajoutés ou supprimés.
Analogie : Liste de courses
Imaginez une liste de courses :
1. Pommes
2. Bananes
3. Oranges
Si vous ajoutez "Fraises" au milieu :
1. Pommes
2. Fraises ← Nouvel élément
3. Bananes ← A changé de position
4. Oranges ← A changé de position
Sans keys : React pense que les éléments 2, 3 et 4 ont tous changé.
Avec keys : React sait qu'un seul élément a été ajouté, et réutilise les autres.
Comment React utilise les keys
Sans key :
<li>Alice</li>
<li>Bob</li>
React compare par position : "élément 1", "élément 2"
Avec key :
<li key="user-1">Alice</li>
<li key="user-2">Bob</li>
React compare par identité : "user-1", "user-2"
Résultat : Meilleures performances et moins de bugs !
Choisir la bonne key
Règle d'or
Utilisez une valeur unique et stable qui identifie chaque élément de façon permanente.
✅ Option 1 : ID unique (recommandé)
La meilleure solution : un identifiant unique fourni par vos données.
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
✅ Option 2 : Propriété unique
Si vos données n'ont pas d'ID, utilisez une autre propriété unique et stable.
const users = [
{ email: 'alice@example.com', name: 'Alice' },
{ email: 'bob@example.com', name: 'Bob' }
];
return (
<ul>
{users.map(user => (
<li key={user.email}>{user.name}</li>
))}
</ul>
);
❌ À éviter : L'index
N'utilisez jamais l'index si la liste peut changer (ajout, suppression, tri).
// ❌ Interdit pour les listes dynamiques
users.map((user, index) => (
<li key={index}>{user.name}</li>
))
Pourquoi l'index pose problème
L'index change quand la liste est modifiée, ce qui trompe React :
// Avant : 3 utilisateurs
<li key={0}>Alice</li>
<li key={1}>Bob</li>
<li key={2}>Charlie</li>
// Après suppression de Bob
<li key={0}>Alice</li> {/* ✅ Même key, même personne */}
<li key={1}>Charlie</li> {/* ❌ Même key, mais personne différente ! */}
Conséquences :
- React pense que "Charlie" a remplacé "Bob" → re-render inutile
- Si vos éléments ont un état (input, checkbox), l'état se retrouve sur le mauvais élément
Exception : Liste statique qui ne change jamais → index acceptable
Exemples pratiques
Exemple 1 : Liste de produits
function ProductList({ products }) {
return (
<div className="product-grid">
{products.map(product => (
<div key={product.id} className="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p className="price">{product.price}€</p>
<button>Ajouter au panier</button>
</div>
))}
</div>
);
}
// Utilisation
const products = [
{ id: 1, name: 'Laptop', price: 999, image: 'laptop.jpg' },
{ id: 2, name: 'Souris', price: 25, image: 'mouse.jpg' },
{ id: 3, name: 'Clavier', price: 75, image: 'keyboard.jpg' }
];
<ProductList products={products} />
Exemple 2 : Tableau de données
function UserTable({ users }) {
return (
<table>
<thead>
<tr>
<th>Nom</th>
<th>Email</th>
<th>Rôle</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{users.map(user => (
<tr key={user.id}>
<td>{user.name}</td>
<td>{user.email}</td>
<td>{user.role}</td>
<td>
<button>Éditer</button>
<button>Supprimer</button>
</td>
</tr>
))}
</tbody>
</table>
);
}
Filtrer avant d'afficher
Combinez .filter() et .map() pour afficher des listes filtrées.
Exemple : Filtrer par catégorie
function ProductList({ products, category }) {
return (
<div>
{products
.filter(product => product.category === category)
.map(product => (
<div key={product.id} className="product">
<h3>{product.name}</h3>
<p>{product.price}€</p>
</div>
))}
</div>
);
}
// Utilisation
const products = [
{ id: 1, name: 'Laptop', price: 999, category: 'electronics' },
{ id: 2, name: 'Shirt', price: 29, category: 'clothing' },
{ id: 3, name: 'Phone', price: 699, category: 'electronics' }
];
<ProductList products={products} category="electronics" />
// Affiche : Laptop, Phone
Exemple : Recherche
function SearchableList({ items, searchTerm }) {
const filteredItems = items.filter(item =>
item.name.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<ul>
{filteredItems.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
Trier avant d'afficher
Utilisez .sort() pour trier les données.
function ProductList({ products, sortBy = 'name' }) {
// Créer une copie pour ne pas modifier l'original
const sortedProducts = [...products].sort((a, b) => {
if (sortBy === 'price') {
return a.price - b.price;
}
return a.name.localeCompare(b.name);
});
return (
<div>
{sortedProducts.map(product => (
<div key={product.id}>
<h3>{product.name}</h3>
<p>{product.price}€</p>
</div>
))}
</div>
);
}
Attention avec .sort()
.sort() modifie le tableau original. Créez une copie avant de trier :
// ❌ Modifie l'original
products.sort((a, b) => a.price - b.price)
// ✅ Crée une copie
[...products].sort((a, b) => a.price - b.price)
Gérer les listes vides
Affichez un message si la liste est vide.
Méthode 1 : Early return (recommandé)
function UserList({ users }) {
if (users.length === 0) {
return <p>Aucun utilisateur trouvé.</p>;
}
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
Méthode 2 : Opérateur &&
function UserList({ users }) {
return (
<div>
{users.length === 0 && <p>Aucun utilisateur trouvé.</p>}
{users.length > 0 && (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)}
</div>
);
}
Listes imbriquées
Vous pouvez avoir des .map() à l'intérieur d'autres .map().
function CategoryList({ categories }) {
return (
<div>
{categories.map(category => (
<div key={category.id} className="category">
<h2>{category.name}</h2>
<ul>
{category.items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
))}
</div>
);
}
// Données
const categories = [
{
id: 1,
name: 'Électronique',
items: [
{ id: 101, name: 'Laptop' },
{ id: 102, name: 'Phone' }
]
},
{
id: 2,
name: 'Vêtements',
items: [
{ id: 201, name: 'T-shirt' },
{ id: 202, name: 'Pantalon' }
]
}
];
Keys dans les listes imbriquées
Chaque niveau de .map() doit avoir ses propres keys :
{categories.map(category => (
<div key={category.id}> {/* Key du parent */}
{category.items.map(item => (
<div key={item.id}> {/* Key de l'enfant */}
{item.name}
</div>
))}
</div>
))}
Les keys doivent être uniques parmi les frères et sœurs, pas globalement.
Quelle est la meilleure key pour une liste d'utilisateurs provenant d'une API ?
Extraire en composant
Pour plus de lisibilité, extrayez les éléments de liste en composants.
Avant : Tout dans un composant
function ProductList({ products }) {
return (
<div>
{products.map(product => (
<div key={product.id} className="product">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>{product.price}€</p>
<button>Ajouter</button>
</div>
))}
</div>
);
}
Après : Composant séparé
function ProductCard({ product }) {
return (
<div className="product">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>{product.price}€</p>
<button>Ajouter</button>
</div>
);
}
function ProductList({ products }) {
return (
<div>
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}
Avantages
Lisibilité : Le code est plus clair et organisé
Réutilisabilité : ProductCard peut être utilisé ailleurs
Maintenabilité : Plus facile de modifier un composant isolé
Important : La key reste sur le composant dans le .map(), pas à l'intérieur !
Où doit se trouver la prop 'key' quand vous extrayez un élément de liste en composant ?
Récapitulatif
Ce que vous avez appris
Vous savez maintenant :
- ✅ Utiliser
.map()pour afficher des listes - ✅ Comprendre pourquoi les keys sont essentielles
- ✅ Choisir de bonnes keys (ID unique > propriété unique > index)
- ✅ Filtrer et trier des listes
- ✅ Gérer les listes vides
- ✅ Créer des listes imbriquées
- ✅ Extraire des éléments en composants
Prochaine étape : Afficher du contenu conditionnel !