React JS. ¿Por qué los elementos de una lista deben llevar una clave?
Una explicación breve y clara de por qué React nos pide que asignemos una key a cada elemento de una lista
La integración de React con JSX es una de las características que más me gustan de esta librería. Gracias a ella podemos seguir trabajando como si estuviéramos escribiendo HTML pero con la ventaja de poder usar componentes y cualquier función de Javascript dentro del código.
Esto permite que sea suficiente con conocer Javascript para escribir JSX, sin necesidad de aprender a trabajar con estructuras propias como sucede en Angular con, por ejemplo, *ngFor*
.
Una de las primeras cosas que aprendemos a hacer cuando empezamos a trabajar con JSX es a renderizar listas. Seguramente que esta sintaxis te resulte familiar:
function MyComponent() {
const numbers = [1, 2, 3];
return (
<ul>
{numbers.map(n => <li key={`number--${n}`}>{n}</li>)}
</ul>
);
}
Pero… ¿por qué React nos pide añadir una clave a cada elemento de la lista? En este artículo quiero explicarte muy brevemente el motivo ya que a menudo pasamos este tipo de cosas por alto (las damos por hecho) y sin embargo son perfectas para entender algunos mecanismos de React.
¡Vamos a verlo!
Listas y rendimiento
Las listas de elementos (especialmente aquellas que tienen un gran tamaño) suelen ser uno de los factores que más suelen afectar al rendimiento ya que exigen al navegador (o cualquier otra interfaz) renderizar muy rápido los elementos que se deben ir mostrando.
Cuando renderizamos listas en React con JSX, se nos pide asignar a cada elemento una clave única que permita identificarlos. Si en el ejemplo anterior omitimos dicha clave:
function MyComponent() {
const numbers = [1, 2, 3];
return (
<ul>
{numbers.map(n => <li>{n}</li>)}
</ul>
);
}
React lanzará un “warning” en la consola advirtiéndonos de lo siguiente:
“Warning: Each child in an array or iterator should have a unique ‘key’ prop. See https://fb.me/react-warning-keys for more information.”
¿Por qué sucede esto?
Las claves en las listas ayudan a React a identificar los elementos que van cambiando a medida que la lista se va renderizando. De este modo, puede identificar los nuevos elementos, aquellos que se han eliminado y los que han sido reordenados.
Para entender bien esta idea vamos a verlo con el siguiente ejemplo:
const products = [
{
id: 4,
name: 'P4'
},
{
id: 1,
name: 'P1'
},
{
id: 3,
name: 'P3'
}
];function MyComponent() {
return (
<ul>
{products.map(p => <li key={`p--${p.id}`}>{p.name}</li>)}
</ul>
);
}
Gracias a la propiedad key
React va a poder identificar cada uno de los li
que representan un producto independientemente de la posición en la que aparezcan dentro la lista. En el DOM virtual tendrá algo similar a esto (a la izquierda lo que hay en el DOM virtual, a la derecha lo que vemos en el navegador):
<li key="p--4">P4</li> --> <li>P4</li><li key="p--1">P1</li> --> <li>P1</li><li key="p--3">P3</li> --> <li>P3</li>
De este modo, si reordenamos la lista, React podrá aprovechar esos li
que ya tiene en el DOM sin necesidad de volver a crear la lista completa: bastará con reordenar los elementos en el DOM.
Es decir, si tras una acción se pasa a tener este orden:
<li key="p--4">P4</li>
<li key="p--3">P3</li>
<li key="p--1">P1</li>
React lo único que tendrá que hacer es mover en el DOM de sitio el producto P1 (identificado con la clave p--1
), sin necesidad de eliminar ese elemento y volver a crearlo debajo de P3.
<li>P4</li>
<li>P3</li>
<li>P1</li>
Lo mismo sucederá si añadimos un elemento al comienzo de la lista. El DOM virtual pasará a ser, por ejemplo:
<li key="p--5">P5</li>
<li key="p--4">P4</li>
<li key="p--3">P3</li>
<li key="p--1">P1</li>
Y cuando React consolide el DOM virtual en el DOM del navegador, verá que tan sólo tendrá que crear un elemento li
, pues los otros elementos ya los tiene creados.
Claves únicas
Por si te estabas haciendo la pregunta, las claves sólo tienen que ser únicas dentro de la misma lista. Es decir, no hay problema en tener dos listas cuyos elementos repitan las claves, ya que para React se encontraran en distintos nodos del árbol y no habrá problema.
Claves e índices
Ahora que ya sabemos la importancia que tienen las claves que asociamos a los elementos de una lista seguramente cobre sentido la recomendación de React para no usar los índices del array como claves, ya que esta práctica supondrá que React no sea capaz de identificar a los elementos.
Como con un ejemplo estas cosas se ven mejor, te dejo el siguiente código:


En el ejemplo volvemos a tener una lista de 3 productos de la cual pasado 1 segundo eliminamos el segundo. Esa animación la estamos realizando con la librería ReactCSSTransitionGroup la cual compara los elementos que había en el render anterior con los nuevos. Lo importante es que esta comprobación la hace en base a las claves que hayamos asignado.
Por tanto, al principio ReactCSSTransitionGroup verá la siguiente lista:
<li key="p--0">P4</li>
<li key="p--1">P1</li>
<li key="p--2">P3</li>
Cuando desaparece el producto de en medio, la lista que verá será:
<li key="p--0">P4</li>
<li key="p--1">P1</li>
Es decir, para ReactCSSTransitionGroup el elemento que ha desaparecido es el que tiene la clave p--2
, animando erróneamente el último elemento en vez de el de en medio.
Puedes ver el ejemplo completo en el Codesandbox que he creado:
Si quieres ver otro ejemplo, te invito a que visites el artículo que escribió Robin Pokorny sobre el uso de las claves.
Por tanto, siempre que sea posible genera identificadores únicos para las claves que no dependan del índice de la iteración.
Conclusiones
Espero que este ejemplo te haya ayudado a comprender por qué React pide que especifiquemos claves en las listas y que estas no dependan de los índices de iteración.
Como decía al principio me gusta escribir este tipo de artículos para profundizar en conceptos y características que usamos habitualmente sin pararnos a pensar el motivo.
¿Quieres recibir más artículos como este?
Si te ha gustado este artículo te animo a que te suscribas a la newsletter que envío cada domingo con publicaciones similares a esta y más contenido recomendado: 👇👇👇
Apóyame en Patreon
🧡🧡🧡 Gracias a: Joseba, Óscar, Alex y Jorge.