Renderizado de listas
A menudo querrás mostrar muchos componentes similares de una colección de datos. Puedes usar los métodos de array de JavaScript para manipular un array de datos. En esta página, usarás filter()
y map()
con React para filtrar y transformar tu array de datos en un array de componentes.
Aprenderás
- Cómo renderizar componentes desde un array usando el método
map()
de JavaScript - Cómo renderizar solo un componente específico usando
filter()
de JavaScript - Cuándo y cómo usar las keys de React
Renderizar datos desde arrays
Digamos que tienes una lista de contenido.
<ul>
<li>Creola Katherine Johnson: matemática</li>
<li>Mario José Molina-Pasquel Henríquez: químico</li>
<li>Mohammad Abdus Salam: físico</li>
<li>Percy Lavon Julian: químico</li>
<li>Subrahmanyan Chandrasekhar: astrofísico</li>
</ul>
La única diferencia entre esos elementos de la lista es su contenido, sus datos. A menudo necesitarás mostrar muchas instancias del mismo componente usando diferentes datos cuando construyas interfaces: desde listas de comentarios a galerías de fotos de perfiles. En estas situaciones, puedes guardar estos datos en objetos de JavaScript y arrays, y usar métodos como map()
y filter()
para renderizar listas de componentes desde ellos.
Aquí hay un corto ejemplo de como generar una lista de elementos de un array:
- Mueve los datos en un array:
const people = [
'Creola Katherine Johnson: matemática',
'Mario José Molina-Pasquel Henríquez: químico',
'Mohammad Abdus Salam: físico',
'Percy Lavon Julian: químico',
'Subrahmanyan Chandrasekhar: astrofísico'
];
- Mapea los miembros de
people
en un nuevo array de nodos JSX,listItems
:
const listItems = people.map(person => <li>{person}</li>);
- Devuelve
listItems
desde tu componente envuelto en un<ul>
:
return <ul>{listItems}</ul>;
Aquí está el resultado:
const people = [ 'Creola Katherine Johnson: matemática', 'Mario José Molina-Pasquel Henríquez: químico', 'Mohammad Abdus Salam: físico', 'Percy Lavon Julian: químico', 'Subrahmanyan Chandrasekhar: astrofísico' ]; export default function List() { const listItems = people.map(person => <li>{person}</li> ); return <ul>{listItems}</ul>; }
Date cuenta que el sandbox anterior muestra un error por consola:
Aprenderás como arreglar este error más adelante en esta página. Antes de que lleguemos a eso, vamos a añadir algo de estructura a tus datos.
Filtrar arrays de objetos
Estos datos pueden ser estructurados incluso más.
const people = [{
id: 0,
name: 'Creola Katherine Johnson',
profession: 'matemática',
}, {
id: 1,
name: 'Mario José Molina-Pasquel Henríquez',
profession: 'químico',
}, {
id: 2,
name: 'Mohammad Abdus Salam',
profession: 'físico',
}, {
name: 'Percy Lavon Julian',
profession: 'químico',
}, {
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrofísico',
}];
Digamos que quieres una manera de mostrar solo las personas cuya profesión sea 'químico'
. Puedes usar el método filter()
de JavaScript para devolver solo esas personas. Este método coge un array de objetos, los pasa por un «test» (una función que devuelve true
o false
), y devuelve un nuevo array de solo esos objetos que han pasado el test (que han devuelto true
).
Tú solo quieres los objetos donde profession
es 'químico'
. La función «test» para esto se ve como (person) => person.profession === 'químico'
. Aquí está cómo juntarlo:
- Crea un nuevo array solo de personas que sean «químicos»,
chemists
, llamando al métodofilter()
enpeople
filtrando porperson.profession === 'químico'
:
const chemists = people.filter(person =>
person.profession === 'químico'
);
- Ahora mapea sobre
chemists
:
const listItems = chemists.map(person =>
<li>
<img
src={getImageUrl(person)}
alt={person.name}
/>
<p>
<b>{person.name}:</b>
{' ' + person.profession + ' '}
conocido/a por {person.accomplishment}
</p>
</li>
);
- Por último, devuelve el
listItems
de tu componente:
return <ul>{listItems}</ul>;
import { people } from './data.js'; import { getImageUrl } from './utils.js'; export default function List() { const chemists = people.filter(person => person.profession === 'químico' ); const listItems = chemists.map(person => <li> <img src={getImageUrl(person)} alt={person.name} /> <p> <b>{person.name}:</b> {' ' + person.profession + ' '} conocido/a por {person.accomplishment} </p> </li> ); return <ul>{listItems}</ul>; }
Mantener los elementos de una lista en orden con key
Fíjate que todos los sandboxes anteriores mostraban un error en la consola:
Tienes que darle a cada elemento del array una key
(una cadena de texto o un número) que lo identifique de manera única entre otros elementos del array:
<li key={person.id}>...</li>
Las keys le indican a React que objeto del array corresponde a cada componente, para así poder emparejarlo más tarde. Esto se vuelve más importante si los objetos de tus arrays se pueden mover (p. ej. debido a un ordenamiento), insertar, o eliminar. Una key
bien escogida ayuda a React a entender lo que ha sucedido exactamente, y hacer las correctas actualizaciones en el árbol del DOM.
En vez de generar keys sobre la marcha, deberías incluirlas en tus datos:
export const people = [{ id: 0, // Usado en JSX como key name: 'Creola Katherine Johnson', profession: 'matemática', accomplishment: 'los cálculos de vuelos espaciales', imageId: 'MK3eW3A' }, { id: 1, // Usado en JSX como key name: 'Mario José Molina-Pasquel Henríquez', profession: 'químico', accomplishment: 'el descubrimiento del agujero de ozono en el Ártico', imageId: 'mynHUSa' }, { id: 2, // Usado en JSX como key name: 'Mohammad Abdus Salam', profession: 'físico', accomplishment: 'la teoría del electromagnetismo', imageId: 'bE7W1ji' }, { id: 3, // Usado en JSX como key name: 'Percy Lavon Julian', profession: 'químico', accomplishment: 'ser pionero en el uso de cortisona, esteroides y píldoras anticonceptivas', imageId: 'IOjWm71' }, { id: 4, // Usado en JSX como key name: 'Subrahmanyan Chandrasekhar', profession: 'astrofísico', accomplishment: 'los cálculos de masa de estrellas enanas blancas', imageId: 'lrWQx8l' }];
Profundizar
¿Qué haces cuándo cada objeto necesita renderizar no uno, sino varios nodos del DOM?
El atajo de sintaxis del <>...</>
Fragment no te dejará pasarle una key, así que necesitas agruparlos en un solo <div>
, o usar una sintaxis algo más larga y más explícita del <Fragment>
:
import { Fragment } from 'react';
// ...
const listItems = people.map(person =>
<Fragment key={person.id}>
<h1>{person.name}</h1>
<p>{person.bio}</p>
</Fragment>
);
Los Fragments desaparecen del DOM, así que esto producirá una lista plana de <h1>
, <p>
, <h1>
, <p>
, y así.
Dónde conseguir tu key
Distintas fuentes de datos dan diferentes fuentes de keys:
- Datos de una base de datos: Si tus datos vienen de una base de datos, puedes usar las keys/ID de la base de datos, que son únicas por naturaleza.
- Datos generados localmente: Si tus datos son generados y persistidos localmente (p. ej. notas en una app de tomar notas), usa un contador incremental,
crypto.randomUUID()
o un paquete comouuid
cuando este creando objetos.
Reglas de las keys
- Las keys tienen que ser únicas entre elementos hermanos. Sin embargo, está bien usar las mismas keys para nodos JSX en arrays diferentes.
- Las keys no tienen que cambiar o ¡eso quitará su propósito! No las generes mientras renderizas.
¿Por qué React necesita keys?
Imagina que los archivos de tu escritorio no tuvieran nombres. En vez de eso, tu te referirías a ellos por su orden — el primer archivo, el segundo, y así. Podrías acostumbrarte a ello, pero una vez borres un archivo, se volvería algo confuso. El segundo archivo se convertiría en el primero, el tercer archivo se convertiría en el segundo, y así.
Los nombres de archivos en una carpeta y las keys JSX en un array tienen un propósito similar. Nos permiten identificar un objeto de manera única entre sus hermanos. Una key bien escogida da más información aparte de la posición en el array. incluso si la posición cambia devido a un reordenamiento, la key
permite a React identificar al elemento a lo largo de su ciclo de vida.
Recapitulación
En esta página has aprendido:
- Como mover datos fuera de componentes y en estructuras de datos como arrays y objetos.
- Como genrerar sets de componentes similares con el método
map()
de JavaScript. - Como crear arrays de objetos filtrados con el método
filter()
de JavaScript. - Por qué y cómo poner la
key
en cada componente en una colección para que React pueda seguir la pista de cada uno de ellos incluso si su posición o datos cambia.
Desafío 1 de 4: Dividir una lista en dos
Este ejemplo muestra una lista de todas las personas.
Cambiala para mostrar dos listas separadas, una detrás de otra : Químicos y Todos los demás. Como antes, puedes saber que persona es química comprobando si person.profession === 'químico'
.
import { people } from './data.js'; import { getImageUrl } from './utils.js'; export default function List() { const listItems = people.map(person => <li key={person.id}> <img src={getImageUrl(person)} alt={person.name} /> <p> <b>{person.name}:</b> {' ' + person.profession + ' '} conocido/a por {person.accomplishment} </p> </li> ); return ( <article> <h1>Científicos</h1> <ul>{listItems}</ul> </article> ); }