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:

  1. 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'
];
  1. Mapea los miembros de people en un nuevo array de nodos JSX, listItems:
const listItems = people.map(person => <li>{person}</li>);
  1. 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:

Console
Warning: Each child in a list should have a unique “key” prop.
(TraducciĂłn)
Advertencia: Cada hijo en una lista debe tener una Ășnica prop “key”.

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',
}, {
id: 3,
name: 'Percy Lavon Julian',
profession: 'quĂ­mico',
}, {
id: 4,
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:

  1. Crea un nuevo array solo de personas que sean “quĂ­micos”, chemists, llamando al mĂ©todo filter() en people filtrando por person.profession === 'quĂ­mico':
const chemists = people.filter(person =>
person.profession === 'quĂ­mico'
);
  1. 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>
);
  1. 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>;
}

AtenciĂłn

Las funciones de flecha implícitamente devuelven la expresión justo después del =>, así que no necesitas declarar un return:

const listItems = chemists.map(person =>
<li>...</li> // Implicit return!
);

Sin embargo, ÂĄdebes escibir el return explĂ­citamente si tu => estĂĄ seguida por una llave{!

const listItems = chemists.map(person => { // Curly brace
return <li>...</li>;
});

Las funciones de flecha que tienen => { se dice que tienen un “cuerpo de bloque”. Te permiten escribir más de una sola línea de código, pero tienes que declarar un return por ti mismo. Si lo olvidas, ¡Nada será devuelto!

Mantener los elementos de una lista en orden con key

FĂ­jate que todos los sandboxes anteriores mostraban un error en la consola:

Console
Warning: Each child in a list should have a unique “key” prop.
(TraducciĂłn)
Advertencia: Cada hijo en una lista debe tener una Ășnica prop “key”.

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>

Nota

ÂĄLos elementos JSX directamente dentro de una llamada a un map() siempre necesitan keys!

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

Mostrar varios nodos DOM para cada elemento de una lista

¿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 como uuid 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 debido a un reordenamiento, la key permite a React identificar al elemento a lo largo de su ciclo de vida.

AtenciĂłn

PodrĂ­as estar tentado a usar el Ă­ndice del elemento en el array como su key. De hecho, eso es lo que React usarĂĄ si tu no especifĂ­cas una key en absoluto. Pero el orden en el que renderizas elementos cambiarĂĄ con el tiempo si un elemento es insertado, borrado, o si se reordena su array. El Ă­ndice como key lleva a menudo a sutiles y confusos errores.

Igualmente, no generes keys sobre la marcha, p. ej. con key={Math.random()}. Esto harå que las keys nunca coincidan entre renderizados, llevando a todos tus componentes y al DOM a recrearse cada vez. No solo es una manera lenta, si no que también pierde cualquier input del usuario dentro de los elementos listados. En vez de eso, usa unas IDs basadas en datos.

Date cuenta de que tus componentes no reciben la key como un prop. Solo es usado como pista para React. Si tus componentes necesitan un ID, se lo tienes que pasar como una prop separada: <Profile key={id} userId={id} />.

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>
  );
}