Renderizzare Liste

Spesso vorrai visualizzare più componenti simili da una collezione di dati. Puoi usare i metodi degli array di JavaScript per manipolare un array di dati. In questa pagina, userai filter() e map() con React per filtrare e trasformare il tuo array di dati in un array di componenti.

Imparerai

  • Come renderizzare componenti da un array usando map() di JavaScript
  • Come renderizzare solo componenti specifici usando filter() di JavaScript
  • Quando e perché usare le React keys

Renderizzare dati da array

Immagina di avere una lista di contenuti.

<ul>
<li>Creola Katherine Johnson: mathematician</li>
<li>Mario José Molina-Pasquel Henríquez: chemist</li>
<li>Mohammad Abdus Salam: physicist</li>
<li>Percy Lavon Julian: chemist</li>
<li>Subrahmanyan Chandrasekhar: astrophysicist</li>
</ul>

L’unica differenza tra questi elementi della lista è il loro contenuto, i loro dati. Avrai spesso bisogno di mostrare diverse istanze dello stesso componente usando dati diversi quando costruisci interfacce: dalle liste di commenti alle gallerie di immagini del profilo. In queste situazioni, puoi memorizzare quei dati in oggetti e array JavaScript e usare metodi come map() e filter() per renderizzare liste di componenti da essi.

Qui un breve esempio di come generare una lista di elementi da un array:

  1. Sposta i dati in un array:
const people = [
'Creola Katherine Johnson: mathematician',
'Mario José Molina-Pasquel Henríquez: chemist',
'Mohammad Abdus Salam: physicist',
'Percy Lavon Julian: chemist',
'Subrahmanyan Chandrasekhar: astrophysicist'
];
  1. Mappa i membri di people in un array di nodi JSX, listItems:
const listItems = people.map(person => <li>{person}</li>);
  1. Ritorna listItems dal tuo componente all’interno di <ul>:
return <ul>{listItems}</ul>;

Qui puoi vedere il risultato:

const people = [
  'Creola Katherine Johnson: mathematician',
  'Mario José Molina-Pasquel Henríquez: chemist',
  'Mohammad Abdus Salam: physicist',
  'Percy Lavon Julian: chemist',
  'Subrahmanyan Chandrasekhar: astrophysicist'
];

export default function List() {
  const listItems = people.map(person =>
    <li>{person}</li>
  );
  return <ul>{listItems}</ul>;
}

Nota che il sandbox sopra visualizza un errore di console:

Console
Warning: Each child in a list should have a unique “key” prop.

Imparerai a risolvere questo errore più avanti in questa pagina. Prima di arrivare a quello, aggiungiamo un po’ di struttura ai tuoi dati.

Filtrare array di elementi

Questi dati possono essere strutturati ancora di più.

const people = [{
id: 0,
name: 'Creola Katherine Johnson',
profession: 'mathematician',
}, {
id: 1,
name: 'Mario José Molina-Pasquel Henríquez',
profession: 'chemist',
}, {
id: 2,
name: 'Mohammad Abdus Salam',
profession: 'physicist',
}, {
id: 3,
name: 'Percy Lavon Julian',
profession: 'chemist',
}, {
id: 4,
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrophysicist',
}];

Diciamo che vuoi ottenere un modo per mostrare solo le persone la cui professione è 'chemist'. Puoi usare il metodo filter() di JavaScript per ritornare solo quelle persone. Questo metodo prende un array di elementi, li passa attraverso un “test” (una funzione che ritorna true o false), e ritorna un nuovo array solo con gli elementi che hanno passato il test (ritornato true).

Vuoi solamente gli elementi dove profession è uguale a'chemist'. La funzione “test” per questo scopo è (person) => person.profession === 'chemist'. Ecco come mettere insieme il tutto:

  1. Crea un nuovo array di sole persone “chemist”, chemists, chiamando filter() su people filtrando per person.profession === 'chemist':
const chemists = people.filter(person =>
person.profession === 'chemist'
);
  1. Adesso mappa chemists:
const listItems = chemists.map(person =>
<li>
<img
src={getImageUrl(person)}
alt={person.name}
/>
<p>
<b>{person.name}:</b>
{' ' + person.profession + ' '}
known for {person.accomplishment}
</p>
</li>
);
  1. Infine, ritorna listItems dal tuo 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 === 'chemist'
  );
  const listItems = chemists.map(person =>
    <li>
      <img
        src={getImageUrl(person)}
        alt={person.name}
      />
      <p>
        <b>{person.name}:</b>
        {' ' + person.profession + ' '}
        known for {person.accomplishment}
      </p>
    </li>
  );
  return <ul>{listItems}</ul>;
}

Insidia

Le arrow function ritornano implicitamente l’espressione che segue =>, quindi non hai bisogno di una dichiarazione return:

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

Tuttavia devi scrivere return esplicitamente se il tuo => è seguito da una { parentesi graffa!

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

Le arrow function che contengono => { sono dette avere un “block body”. Ti permettono di scrivere più di una singola linea di codice, ma devi scrivere tu stesso una dichiarazione return. Se la dimentichi, non viene ritornato nulla!

Manetenere liste di elementi in ordine con key

Avrai notato che tutte le sandbox sopra mostrano un errore nella console:

Console
Warning: Each child in a list should have a unique “key” prop.

Devi assegnare ad ogni elemento dell’array una key — una stringa o un numero che lo identifica univocamente tra gli altri elementi di quell’array:

<li key={person.id}>...</li>

Nota bene

Gli elementi JSX direttamente all’interno di un map() hanno sempre bisogno di una key!

Le key dicono a React a quale elemento dell’array corrisponde ogni componente, così che possa associarli in seguito. Questo diventa importante se gli elementi dell’array possono muoversi (ad esempio a causa di un ordinamento), essere inseriti o eliminati. Una key ben scelta aiuta React a capire cosa è successo esattamente, e a fare gli aggiornamenti corretti all’albero del DOM.

Piuttosto che generare le key al volo, dovresti includerle nei tuoi dati:

export const people = [{
  id: 0, // Usato in JSX come key
  name: 'Creola Katherine Johnson',
  profession: 'mathematician',
  accomplishment: 'spaceflight calculations',
  imageId: 'MK3eW3A'
}, {
  id: 1, // Usato in JSX come key
  name: 'Mario José Molina-Pasquel Henríquez',
  profession: 'chemist',
  accomplishment: 'discovery of Arctic ozone hole',
  imageId: 'mynHUSa'
}, {
  id: 2, // Usato in JSX come key
  name: 'Mohammad Abdus Salam',
  profession: 'physicist',
  accomplishment: 'electromagnetism theory',
  imageId: 'bE7W1ji'
}, {
  id: 3, // Usato in JSX come key
  name: 'Percy Lavon Julian',
  profession: 'chemist',
  accomplishment: 'pioneering cortisone drugs, steroids and birth control pills',
  imageId: 'IOjWm71'
}, {
  id: 4, // Usato in JSX come key
  name: 'Subrahmanyan Chandrasekhar',
  profession: 'astrophysicist',
  accomplishment: 'white dwarf star mass calculations',
  imageId: 'lrWQx8l'
}];

Approfondimento

Visualizzare vari nodi DOM per ogni elemento della lista

Cosa farai se ogni elemento della lista deve renderizzare non uno, ma diversi nodi DOM?

La breve sintassi Fragment <>...</> non ti permette di passare una key, quindi devi o raggrupparli in un singolo <div>, o usare la sintassi leggermente più lunga e più esplicita <Fragment>:

import { Fragment } from 'react';

// ...

const listItems = people.map(person =>
<Fragment key={person.id}>
<h1>{person.name}</h1>
<p>{person.bio}</p>
</Fragment>
);

I Fragment scompaiono dal DOM, quindi questo produrrà una lista piatta di <h1>, <p>, <h1>, <p>, e così via.

Da dove prendere la key

Diverse fonti di dati forniscono diverse fonti di key:

  • Dati provenienti da un database: Se i tuoi dati provengono da un database, puoi usare le chiavi / ID del database, che sono uniche per natura.
  • Dati generati localmente: Se i tuoi dati sono generati e mantenuti localmente (ad esempio note in un’app per prendere appunti), usa un contatore crescente, crypto.randomUUID() o un pacchetto come uuid quando crei gli elementi.

Regole delle key

  • Le key devono essere uniche tra i loro simili. Tuttavia, è possibile utilizzare le stesse key per i nodi JSX in array diversi.
  • Le key non devono cambiare o questo ne annulla lo scopo! Non generarle durante il rendering.

Perchè React ha bisogno delle key?

Immagina se i file sul tuo desktop non avessero nomi. Al contrario, ti riferiresti a loro in base al loro ordine: il primo file, il secondo file e così via. Potresti abituarti, ma una volta eliminato un file, sarebbe confuso. Il secondo file diventerebbe il primo file, il terzo file sarebbe il secondo file e così via.

I nomi dei file in una cartella e le key JSX in un array servono a uno scopo simile. Ci consentono di identificare univocamente un elemento tra i suoi simili. Una key ben scelta fornisce più informazioni rispetto alla posizione all’interno dell’array. Anche se la posizione cambia a causa del riordinamento, la key consente a React di identificare l’elemento per tutta la sua durata.

Insidia

Potresti essere tentato di usare l’indice di un elemento nell’array come sua key. In realtà, è quello che React userà se non specifici una key affatto. Ma l’ordine in cui renderizzi gli elementi cambierà nel tempo se un elemento viene inserito, eliminato o se l’array viene riordinato. L’indice come chiave spesso porta a bug impercettibili e confusi.

Similmente, non generare le key al volo, ad esempio con key={Math.random()}. Questo farà sì che le chiavi non corrispondano mai tra i render, portando a tutti i tuoi componenti e DOM che vengono ricreati ogni volta. Non solo questo è lento, ma perderà anche qualsiasi input dell’utente all’interno degli elementi della lista. Invece, usa un ID stabile basato sui dati.

Nota che il tuo componente non riceverà key come prop. Viene utilizzato solo come suggerimento da React stesso. Se il tuo componente ha bisogno di un ID, devi passarlo come prop separata: <Profile key={id} userId={id} />.

Riepilogo

In questa pagina hai imparato:

  • Come muovere i dati fuori dai componenti e in strutture dati come array e oggetti.
  • Come generare una lista componenti simili con map() di JavaScript.
  • Come creare array di elementi filtrati con filter() di JavaScript.
  • Il perchè e il come impostare key su ogni componente in una collezione in modo che React possa tener traccia di ognuno di essi anche se la loro posizione o i dati cambiano.

Sfida 1 di 4:
Dividere una lista in due

Questo esempio mostra una lista di tutte le persone. Cambialo in modo da mostrare due liste separate una dopo l’altra: Chemists e Everyone Else.. Come in precedenza, puoi determinare se una persona è un chimico controllando se person.profession === 'chemist'.

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 + ' '}
        known for {person.accomplishment}
      </p>
    </li>
  );
  return (
    <article>
      <h1>Scientists</h1>
      <ul>{listItems}</ul>
    </article>
  );
}