Para renderizar una lista en React, llama a map() sobre un array y devuelve un trozo de JSX para cada elemento. Dale al elemento más externo de cada item una prop key con un valor que identifique ese item, normalmente su ID, para que React pueda distinguir los elementos cuando la lista cambia.
Agrega { id: 'dat', name: 'Date', price: 4 } al array y aparece una cuarta línea. JSX no tiene una sintaxis de bucle propia: map devuelve un array de elementos, y React renderiza un array renderizando cada elemento en orden.
Renderizar arrays con map
map llama a tu función una vez por elemento y reúne los resultados en un array nuevo. Dentro de las llaves de JSX ese array es solo un valor, así que puedes mapear directamente en el marcado, como arriba, o construir el array antes:
const items = fruits.map((fruit) => <li key={fruit.id}>{fruit.name}</li>);
return <ul>{items}</ul>;
Dos detalles suelen confundir. Si usas llaves en la función flecha, necesitas un return: fruits.map((f) => { return <li key={f.id}>{f.name}</li>; }). Sin él, la función devuelve undefined y la lista queda vacía. Y la key va en el elemento que devuelve map, no en un elemento dentro de él. Si el item es un componente, pon la key en el componente: <FruitRow key={fruit.id} fruit={fruit} />.
Filtrar y luego mapear
Para mostrar solo algunos elementos, filtra primero el array y mapea el resultado. Cada método hace un trabajo, y la cadena se lee como la frase "las frutas que coinciden, como elementos de lista".
Escribe er en la caja de búsqueda y la lista se reduce a Eraser, Ruler y Stapler; marca In stock only y Ruler también desaparece. La lista filtrada se calcula durante el renderizado a partir de las dos piezas de estado, así que no hay un segundo array en el estado que mantener sincronizado. visible.length === 0 && es un uso seguro de && porque la comparación es un booleano real (la página de renderizado condicional muestra qué pasa con un 0 suelto).
Qué hacen las keys
Cuando una lista se vuelve a renderizar, React compara el nuevo array de elementos con el anterior. Las keys son la forma de emparejarlos: el elemento con key 3 ahora es el elemento con key 3 de antes, aunque se haya movido. React conserva el nodo DOM de ese item y el estado de su componente, y solo mueve o actualiza lo que cambió. Los elementos con keys nuevas se crean, y las keys que desaparecieron se eliminan.
Sin keys, lo único por lo que React puede emparejar es la posición. Eso está bien mientras nada se mueve, y está mal en cuanto algo se mueve.
Por qué fallan las keys de índice
map pasa el índice como segundo argumento, así que key={index} es tentador. El índice describe una posición, no un elemento. Inserta un elemento arriba y el índice de cada elemento se desplaza en uno, así que React cree que el item 0 sigue siendo el item 0 y conserva ahí su estado viejo.
La demo de abajo renderiza la misma lista dos veces, una con key por índice y otra por ID. Cada fila tiene un input que guarda su propio texto.
Escribe math junto a Ada en ambas listas y luego haz clic en Add to top. En la lista por ID la nota se queda con Ada. En la lista por índice la nota salta a la persona nueva, porque el primer input sigue siendo el input de la posición 0, y la posición 0 ahora es otra persona. Los nombres están bien en ambas listas, porque vienen de los datos; solo falla el estado que vive en el DOM y en los componentes. Las apps reales se topan con esto en casillas, foco, animaciones y cualquier componente que tenga useState.
Las keys de índice solo son seguras cuando se cumple todo esto: la lista nunca se reordena ni se filtra, nunca se insertan ni se eliminan elementos excepto al final, y los elementos no tienen estado propio. Una lista estática de enlaces del pie de página cumple. Cualquier cosa que el usuario pueda editar normalmente no.
De dónde salen las keys
La mejor key es un ID que ya está en los datos: un ID de base de datos, el SKU de un producto, un slug, un nombre de usuario. Los datos de un servidor casi siempre tienen uno.
Para elementos creados en el navegador, dale a cada elemento un ID al crearlo y guárdalo con el elemento: un contador, como en la demo de arriba, o crypto.randomUUID(). Nunca generes la key durante el renderizado:
// Wrong: a new key on every render, so React remounts every item every time
{todos.map((todo) => <Todo key={Math.random()} todo={todo} />)}
// Right: the ID is created once, in the event that adds the item
function addTodo(text) {
setTodos([...todos, { id: crypto.randomUUID(), text }]);
}
Una key que cambia en cada renderizado es peor que un índice: cada elemento se destruye y se vuelve a crear cada vez, los inputs pierden el foco mientras escribes y nada conserva su estado. El mismo truco es útil a propósito: cambiar la key de un componente lo reinicia, algo que la página de useState usa para vaciar un formulario.
Las keys solo son únicas entre hermanos
Una key tiene que ser única dentro de una lista, no en toda la app. Dos listas separadas pueden reutilizar los mismos IDs, porque React solo compara keys entre los hijos del mismo padre.
Ambas secciones tienen platos con keys 1 y 2, y eso es correcto: cada ul es su propia lista. El problema son las keys duplicadas dentro de una misma lista: React no puede distinguir los elementos que chocan, y cuando la lista se actualiza puede duplicarlos o perderlos.
Cuando un item renderiza varios elementos hermanos, como un <dt> y un <dd>, envuélvelos en <Fragment key={item.id}>. La sintaxis corta <> no puede recibir una key.
El aviso de "unique key prop"
Si omites la key, React imprime esto en la consola del navegador durante el desarrollo:
Each child in a list should have a unique "key" prop.
Check the render method of `App`.
La segunda línea nombra el componente cuyo renderizado produjo la lista, lo que te dice qué map arreglar. Dos hermanos con la misma key reciben un mensaje distinto, que empieza con "Encountered two children with the same key". La vista previa de esta página funciona como un build de producción, que omite esa comprobación, así que aquí no verás el aviso aunque borres una key. Borra una de todas formas en el primer ejemplo: la lista sigue renderizándose, porque una key ausente es un riesgo de corrección, no un fallo. Para arreglar el aviso, agrega una key estable al elemento que devuelve map; agregar key={index} lo silencia pero trae de vuelta el error mostrado arriba.
Ten en cuenta también que key no se pasa a tu componente. Dentro de FruitRow, props.key es undefined. Si el componente necesita el ID, pásalo una segunda vez como prop normal: <FruitRow key={f.id} id={f.id} />.
Preguntas frecuentes
¿Por qué React necesita una key en los elementos de una lista?
Las keys le dicen a React qué elemento es cuál entre renderizados. Cuando el array cambia, React empareja los elementos viejos y nuevos por key, así puede conservar el estado y el nodo DOM de cada elemento con los datos correctos en lugar de adivinar por posición.
¿Puedo usar el índice del array como key?
Solo para una lista que nunca cambia de orden y nunca recibe ni pierde elementos en medio. Si los elementos pueden moverse, una key de índice ata el estado a la posición, así que el texto escrito, las casillas y el foco terminan en el elemento equivocado.
¿Qué debo usar como key?
Un ID que pertenezca a los datos: un ID de base de datos, el SKU de un producto, un nombre de usuario. Para elementos creados en el navegador, genera un ID una sola vez al crear el elemento (un contador o crypto.randomUUID()), nunca durante el renderizado.
¿Las keys tienen que ser únicas en toda la app?
No. Las keys solo tienen que ser únicas entre los hermanos de la misma lista. Dos listas distintas pueden usar las mismas keys.
¿Un componente puede leer su propia key?
No. React usa key y no la pasa al componente como prop. Si el componente necesita el ID, pásalo otra vez con otro nombre, como id={item.id}.
¿Cómo arreglo "Each child in a list should have a unique key prop"?
Agrega una key al elemento más externo que devuelves desde map, usando un ID estable del elemento. Si ese elemento es un fragmento, usa <Fragment key={id}>, ya que <> no puede recibir una key.