03/01/2025REACT

Les key de Array.map(), comment ça marche ?

📌

Les key, c’est ce qu’on doit renseigner lorsqu’on utilise map pour render du DOM. On doit renseigner une valeur unique pour chaque élément, mais pourquoi ? Explications.

Le fonctionnement des key

Que se passe-t-il lorsque l’Array est mis à jour ?

Lorsque l’on met à jour le tableau (ex: via un setState), React va comparer les différentes key fournies par le tableau d’avant/après la mise à jour.

  • Si la key existe encore, React va uniquement re render le composant (maj)
  • Si la key n’existe plus, elle sera démontée (supprimée)
  • Si la key n’existait pas, elle est render (créée)

Exemple

Imaginons que nous trions le tableau d’asc à desc :

key 0 : “rouge” → key 0 : “bleu” (re render)

key 1 : “bleu” → key 1 : “rouge” (re render)

On voit que les noms des keys restent les même : 0 et 1. Seules les valeurs attribuées à ces key changent ; elles sont donc mises à jour.

Pourquoi pas d’index dans key ?

On a vu dans l’exemple précédent que si l’on attribue au key l’index fournit par le map, chaque key n’est ni supprimée ni créée mais mise à jour.

Essayons maintenant avec un id unique :

key ‘RO’ : “rouge” → key ‘BL’ : “bleu” (rien ne se passe)

key ‘BL’ : “bleu” → key ‘RO’ : “rouge” (rien ne se passe)

Comme avec les index, il y a toujours les même noms de key, cependant les valeurs attribuées aux keys ‘RO’ et ‘BL’ sont toujours les mêmes, il n’y a donc aucune mise à jour à faire.

Pourquoi pas d’id dans key ?

Imaginons une pagination :

key ‘RO’ : “rouge” → page 2 → key ‘VI’ : “violet” (render de 0)

key ‘BL’ : “bleu” → page 2 → key ‘JA’ : “jaune” (render de 0)

Les id ont complètement changés puisque ce ne sont plus du tout les même éléments qui sont affichés (contrairement à un tri simple). Du coup, React va devoir démonter les keys supprimées + monter les nouvelles keys.


key 0 : “rouge” → page 2 → key 0 : “violet” (re render)

key 1 : “bleu” → page 2 → key 1 : “jaune” (re render)

Ici il y a toujours les même keys, simplement plus les même valeurs attribuées : elles sont juste mises à jour.


⚠️

Si key avec index → le composant retourné dans le map ne doit pas être memo s’il contient des states : Comme il y a juste un re render et pas un mount du composant, le state n’est pas réinitialisé. Par exemple, le mail 1 sera sélectionné (key 0), on passe à la page 2, et celui qui aura la key 0, disons mail 21, sera sélectionné à sa place alors qu’il n’a rien à voir.

Exemple de tests pour un tri

Sans memo

→ Enfant re render quoi qu’il se passe avec la key puisque le parent re render

  • Avec index :
const Key = () => {
    let init = [
        { id: 'RE', name: 'red' },
        { id: 'BL', name: 'blue' },
    ];

    const [colors, setColors] = useState(init);

    const handleClick = () => {
        const reversed = [...colors].reverse();
        setColors(reversed);
    };

    return (
        <div>
            <button onClick={handleClick}>Click</button>
            {colors.map((e, i) => (
                <ItemMemo color={e} key={i} />
            ))}
        </div>
    );
};

export default Key;

const Item = ({ color }) => {
    console.log('render');
    useEffect(() => console.log('mount color'), [color]);
    useEffect(() => console.log('mount'), []);

    return <p>{color.name}</p>
    );
};

// render x2
// mount color (le key 0)
// mount (le key 1)
// mount color (le key 0)
// mount (le key 1)
// ------ tri du tableau ------
// render x2
// mount color x2

On a plus les mêmes valeurs dans les keys (=props color associé à la key qui a changé). Donc comme state update → render → et on passe dans le useEffect de color

  • Avec id :
...
<ItemMemo color={e} key={e.id} />
...

// pareil
// ------ tri du tableau ------
// render x2

La props color associée à la key n’a pas changé : on a uniquement un re render car le parent a re render (puisqu’on a pas de memo)

Avec memo

→ Si la props ne change pas, pas de re render enfant

  • Avec index :
...
<ItemMemo color={e} key={i} />
...
const Item = memo({ color }) => { ... });

// pareil
// ------ tri du tableau ------
// render x2
// mount color x2

Toujours la même situation qu’avant. Comme la props a changé, le memo n’a aucun effet.

  • Avec id :
...
<ItemMemo color={e} key={e.id} />
...
const Item = memo({ color }) => { ... });

// pareil
// ------ tri du tableau ------
// (rien)

Cette fois, comme la props n’a pas changé ET qu’il y a un memo sur le composant Item, il ne va pas re render.