El renderizado de colecciones en React se refiere a tomar una colección (normalmente un array) y transformarlo en una colección de elementos JSX para ser renderizados en la interfaz.
Por ejemplo lista de productos de un e-commerce, los comentarios de una web, o los usuarios de un panel de administración.
En este artículo vamos a ver cómo React convierte esos Arrays de datos en Arrays de componentes visuales.
El método .map()
Si venís de la programación imperativa (programación “normal”), vuestro instinto seguramente sea usar un bucle for o un forEach para iterar sobre los datos y pintar cosas.
Pero recordemos que en JSX solo podemos usar expresiones (cosas que devuelven un valor). Y un bucle for… no devuelve nada 😉.
Por eso, en React el método estándar para renderizar listas es .map()
El método .map() es perfecto porque transforma un array en otro array del mismo tamaño. Nosotros tomamos un array de datos (objetos, strings) y lo transformamos en un array de elementos JSX.
const frutas = ['Manzana', 'Banana', 'Cereza'];
function ListaFrutas() {
// Transformamos strings -> <li>
const elementosLista = frutas.map((fruta) => {
return <li>{fruta}</li>;
});
return (
<ul>
{elementosLista}
</ul>
);
}
Generalmente, hacemos esto directamente dentro del JSX (inline) para ahorrar código:
function ListaFrutas() {
const frutas = ['Manzana', 'Banana', 'Cereza'];
return (
<ul>
{frutas.map((fruta) => (
<li>{fruta}</li>
))}
</ul>
);
}
React es lo suficientemente inteligente como para recibir un array de elementos JSX y pintarlos uno detrás de otro.
Renderizando componentes
Por supuesto, no estamos limitados a etiquetas HTML simples como <li>. Lo podemos usarlo para renderizar componentes enteros.
Imaginad que tenemos un array de objetos:
const usuarios = [
{ id: 1, nombre: 'Luis', rol: 'Admin' },
{ id: 2, nombre: 'María', rol: 'User' },
{ id: 3, nombre: 'Carlos', rol: 'Editor' }
];
function UserList() {
return (
<div className="grid">
{usuarios.map((usuario) => (
// Pasamos todo el objeto o props individuales
<UserCard
nombre={usuario.nombre}
rol={usuario.rol}
/>
))}
</div>
);
}
Hasta aquí todo parece correcto. El código funciona y vemos la lista. Pero si abrimos la consola del navegador (F12), veremos una advertencia en rojo chillón.
Warning: Each child in a list should have a unique “key” prop.
Acabas de encontrar uno de los mensajes de advertencia más habituales y famosos de React. ¿Por qué pasa? Lo vemos ahora mismo 👇.
La importancia de la key
El mensaje anterior se queja de que no has definido la prop key. Este es un atributo especial que debemos incluir al crear listas de elementos.
// Forma correcta
<UserCard key={usuario.id} nombre={usuario.nombre} ... />
Cada elemento de una lista renderizada con .map() debe tener una propiedad especial llamada key. Esta clave permite a React identificar cada ítem de manera única al actualizar o reordenar elemento.
Reglas para usar keys
- Deben ser únicas: Cada key debe ser única dentro de la lista. No uses el índice del array como key a menos que no tengas otra opción.
- Deben ser estables: Las keys no deben cambiar entre renderizados. Por ejemplo, no uses un valor aleatorio como key.
¿Qué usar como Key?
IDs únicos (la mejor opción)
Lo ideal es usar datos que vengan de vuestra base de datos: IDs numéricos, UUIDs, Slugs, DNI, etc. Algo que identifique unívocamente a ese dato en el universo.
{productos.map(producto => (
<Producto key={producto.id} data={producto} />
))}
Índices del Array (la mala opción)
Es muy tentador usar el segundo argumento de .map() (el índice) como key.
// ¡EVITA ESTO SI ES POSIBLE!
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
Usar el índice silencia el warning de la consola, pero no soluciona el problema.
Si la lista es estática (nunca cambia, nunca se reordena, nunca se filtra), usar el índice es aceptable.
Pero si la lista puede cambiar de orden, borrar elementos o insertarlos, usar el índice provocará bugs.
Nunca ID con Math.random()
Nunca, bajo ningún concepto, generéis keys al vuelo con key={Math.random()}.
Esto hará que las keys cambien en cada renderizado, forzando a React a destruir y crear todos los elementos de la lista constantemente.
El rendimiento caerá en picado y perderéis el foco de los inputs (os despedirán, y posiblemente se te marchiten las amapolas 🌻).
Detalles técnicos a tener en cuenta
- Las Keys deben ser únicas entre hermanos: No necesitan ser únicas en toda la app (si lo son, incluso mejor), solo dentro de su lista padre.
- La Key se define en el
.map(): Si extraéis el elemento a un componente, la key va en la llamada al componente, no dentro del HTML del componente.
// ❌ MAL
function Lista() {
return items.map(item => <Item item={item} />);
}
function Item({ item }) {
return <li key={item.id}>{item.nombre}</li>; // La key aquí no sirve
}
// ✔️ BIEN
function Lista() {
// La key va en el elemento más externo que devuelve el map
return items.map(item => <Item key={item.id} item={item} />);
}
- La Key no es una Prop: Si intentáis leer
props.keydentro del componente hijo, veréis que esundefined. React se “come” la key para su uso interno y no la pasa hacia abajo. Si necesitáis el ID dentro del componente, pasadlo en otra prop explícita (ej:id={usuario.id}).
