Često ćete želeti da prikažete više sličnih komponenata iz kolekcije podataka. Možete koristiti JavaScript metode za nizove kako bi manipulisali nizovima podataka. U ovom članku, koristićete filter() i map() da uz React filtrirate i transformišete vaš niz podataka u niz komponenata.

Naučićete:

  • Kako da renderujete komponente na osnovu niza uz pomoć JavaScript-ovog map()-a
  • Kako da renderujete samo specifične komponente uz pomoć JavaScript-ovog filter()-a
  • Kada i zašto da koristite React ključeve

Renderovanje podataka iz nizova

Recimo da imate sledeću listu.

<ul>
<li>Creola Katherine Johnson: matematičar</li>
<li>Mario José Molina-Pasquel Henríquez: hemičar</li>
<li>Mohammad Abdus Salam: fizičar</li>
<li>Percy Lavon Julian: hemičar</li>
<li>Subrahmanyan Chandrasekhar: astrofizičar</li>
</ul>

Jedina razlika u stavkama liste je njihov sadržaj, njihovi podaci. Često ćete imati potrebu da prikažete nekoliko instanci iste komponente sa drugačijim podacima kada pravite interfejse: od liste komentara do galerije profilnih slika. U takvim situacijama, možete čuvati podatke u JavaScript objektima i nizovima i upotrebiti metode kao što su map() i filter() da od njih renderujete listu komponenata.

Evo kratkog primera kako da generišete listu stavki na osnovu niza:

  1. Pomerite podatke u niz:
const people = [
'Creola Katherine Johnson: matematičar',
'Mario José Molina-Pasquel Henríquez: hemičar',
'Mohammad Abdus Salam: fizičar',
'Percy Lavon Julian: hemičar',
'Subrahmanyan Chandrasekhar: astrofizičar'
];
  1. Mapirajte people članove u novi niz JSX čvorova, listItems:
const listItems = people.map(person => <li>{person}</li>);
  1. Vratite listItems iz vaše komponente umotano u <ul>:
return <ul>{listItems}</ul>;

Evo rezultata:

const people = [
  'Creola Katherine Johnson: matematičar',
  'Mario José Molina-Pasquel Henríquez: hemičar',
  'Mohammad Abdus Salam: fizičar',
  'Percy Lavon Julian: hemičar',
  'Subrahmanyan Chandrasekhar: astrofizičar'
];

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

Primetite da sandbox iznad prikazuje grešku u konzoli:

Console
Warning: Each child in a list should have a unique “key” prop.
Console
Upozorenje: Svako dete u listi bi trebalo da ima jedinstven “key” prop.

Naučićete kako da popravite grešku malo kasnije u ovom članku. Pre toga, hajde da malo strukturiramo podatke.

Filtriranje niza stavki

Ovi podaci mogu dodatno biti strukturirani.

const people = [{
id: 0,
name: 'Creola Katherine Johnson',
profession: 'matematičar',
}, {
id: 1,
name: 'Mario José Molina-Pasquel Henríquez',
profession: 'hemičar',
}, {
id: 2,
name: 'Mohammad Abdus Salam',
profession: 'fizičar',
}, {
id: 3,
name: 'Percy Lavon Julian',
profession: 'hemičar',
}, {
id: 4,
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrofizičar',
}];

Recimo da želite pronaći način da prikažete samo ljude čija je profesija 'hemičar'. Možete koristiti JavaScript-ovu filter() metodu da vratite samo te ljude. Ova metoda prima niz stavki, propušta ih kroz “test” (funkcija koja vraća true ili false), i vraća novi niz samo onih stavki koje su prošle test (vraćeno je true).

Želite samo stavke gde je profession jednako 'hemičar'. Ta “test” funkcija u ovom slučaju izgleda (person) => person.profession === 'hemičar'. Evo kako to možete uraditi:

  1. Napravite novi niz samo “hemičara”, chemists, pozivanjem filter() nad people i filtriranjem person.profession === 'hemičar':
const chemists = people.filter(person =>
person.profession === 'hemičar'
);
  1. Sada mapirajte kroz chemists:
const listItems = chemists.map(person =>
<li>
<img
src={getImageUrl(person)}
alt={person.name}
/>
<p>
<b>{person.name}:</b>
{' ' + person.profession + ' '}
poznat je zbog {person.accomplishment}
</p>
</li>
);
  1. Na kraju, vratite listItems iz vaše komponente:
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 === 'hemičar'
  );
  const listItems = chemists.map(person =>
    <li>
      <img
        src={getImageUrl(person)}
        alt={person.name}
      />
      <p>
        <b>{person.name}:</b>
        {' ' + person.profession + ' '}
        poznat je zbog {person.accomplishment}
      </p>
    </li>
  );
  return <ul>{listItems}</ul>;
}

Pitfall

Arrow funkcije implicitno vraćaju izraz desno od =>, tako da vam ne treba return iskaz:

const listItems = chemists.map(person =>
<li>...</li> // Implicitno vraćanje!
);

Međutim, morate napisati return eksplicitno ako se nakon => nalazi { vitičasta zagrada!

const listItems = chemists.map(person => { // Vitičasta zagrada
return <li>...</li>;
});

Za arrow funkcije koje sadrže => { se kaže da imaju “blok telo”. Omogućavaju vam da napišete više od jedne linije koda, ali morate da napišete return iskaz samostalno. Ako ga zaboravite, ništa neće biti vraćeno!

Čuvanje redosleda stavki u listi sa key

Primetite da svi sandbox-ovi iznad prikazuju grešku u konzoli:

Console
Warning: Each child in a list should have a unique “key” prop.
Console
Upozorenje: Svako dete u listi bi trebalo da ima jedinstven “key” prop.

Svakom članu niza morate dodeliti key — string ili broj koji ga jedinstveno identifikuje u tom nizu:

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

Napomena

JSX elementima unutar map() poziva su uvek potrebni ključevi!

Ključevi govore React-u koji član niza odgovara kojoj komponenti, kako bi mogao kasnije da ih poveže. Ovo postaje značajno ako članovi niza mogu da se pomeraju (npr. prilikom sortiranja), ubacuju, ili brišu. Dobro odabran key pomaže React-u da zaključi šta se zapravo dogodilo, kako bi ispravno mogao da ažurira DOM stablo.

Umesto da generišete ključeve u hodu, trebalo bi da ih uključite u vaše podatke:

export const people = [{
  id: 0, // Koristi se u JSX kao ključ
  name: 'Creola Katherine Johnson',
  profession: 'matematičar',
  accomplishment: 'formula za svemirske letove',
  imageId: 'MK3eW3A'
}, {
  id: 1, // Koristi se u JSX kao ključ
  name: 'Mario José Molina-Pasquel Henríquez',
  profession: 'hemičar',
  accomplishment: 'otkriće Arktičke rupe u ozonu',
  imageId: 'mynHUSa'
}, {
  id: 2, // Koristi se u JSX kao ključ
  name: 'Mohammad Abdus Salam',
  profession: 'fizičar',
  accomplishment: 'teorija o elektromagnetizmu',
  imageId: 'bE7W1ji'
}, {
  id: 3, // Koristi se u JSX kao ključ
  name: 'Percy Lavon Julian',
  profession: 'hemičar',
  accomplishment: 'pionirski kortizon, steroidi i pilule za kontrolu rađanja',
  imageId: 'IOjWm71'
}, {
  id: 4, // Koristi se u JSX kao ključ
  name: 'Subrahmanyan Chandrasekhar',
  profession: 'astrofizičar',
  accomplishment: 'računanje mase belog patuljka',
  imageId: 'lrWQx8l'
}];

Deep Dive

Prikazivanje nekoliko DOM čvorova za svaku stavku u listi

Šta raditi kad svaka stavka u listi treba renderovati ne jedan, već nekoliko DOM čvorova?

Kratka <>...</> Fragment sintaksa vam ne dopušta da prosledite ključ, tako da ih morate grupisati u jedan <div>, ili prosto upotrebiti malo dužu i eksplicitniju <Fragment> sintaksu:

import { Fragment } from 'react';

// ...

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

Fragment-i nestaju iz DOM-a, tako da ćete dobiti listu od <h1>, <p>, <h1>, <p>, i tako dalje.

Gde dobiti key

Različiti izvori podataka pružaju različite ključeve:

  • Podaci iz baze podataka: Ako podaci dolaze iz baze podataka, možete koristiti ključeve ili ID-eve iz baze podataka, koji su po prirodi jedinstveni.
  • Lokalno generisani podaci: Ako su vam podaci generisani i čuvani lokalno (npr. beleške u aplikaciji za zabeleške), koristite inkrementalni brojač, crypto.randomUUID() ili pakete poput uuid kada kreirate stavke.

Pravila ključeva

  • Ključevi moraju biti jedinstveni između „sestrinskih” stavki. Međutim, u redu je koristiti iste ključeve za JSX čvorove u različitim nizovima.
  • Ključevi se ne smeju menjati ili će im se svrha obesmisliti! Nemojte ih generisati tokom renderovanja.

Zašto su React-u potrebni ključevi?

Zamislite da fajlovi na vašem desktop-u nemaju imena. Umesto toga, referencirali bi ih po njihovom redosledu — prvi fajl, drugi fajl, i tako dalje. Mogli biste se navići, ali jednom kada obrišete fajl, postalo bi zbunjujuće. Drugi fajl bi postao prvi fajl, treći fajl bi postao drugi fajl, i tako dalje.

Imena fajlova u folderu i JSX ključevi imaju sličnu ulogu. Omogućavaju nam da jedinstveno identifikujemo stavku među „sestrinskim” stavkama. Dobro odabran ključ pruža više informacija od puke pozicije u nizu. Čak iako se pozicija promeni zbog promene redosleda, key omogućava React-u da identifikuje stavku tokom njenog životnog veka.

Pitfall

Možete biti u iskušenju da koristite indeks člana niza kao njegov ključ. U suštini, to je ono što će React koristiti ako ne specificirate key. Ali, redosled u kojem renderujete stavke će se menjati tokom vremena ako se neka stavka ubaci, obriše, ili se promeni redosled niza. Indeks kao ključ često dovodi do suptilnih i zbunjujućih bug-ova.

Slično tome, nemojte generisati ključeve u hodu, npr. pomoću key={Math.random()}. Ovo će učiniti da se ključevi ne podudaraju između renderovanja, što znači da će sve vaše komponente, kao i DOM, biti ponovo kreirane svaki put. Ne samo što je sporo, već ćete izgubiti bilo koji korisnički input unutar stavke liste. Umesto toga, koristite stabilan ID baziran na podacima.

Obratite pažnju da vaše komponente neće primiti key kao prop. Njega sam React koristi kao nagoveštaj. Ako je vašoj komponenti potreban ID, morate ga proslediti kao poseban prop: <Profile key={id} userId={id} />.

Recap

Na ovoj stranici naučili ste:

  • Kako da premestite podatke iz komponenti u strukture podataka poput nizova i objekata.
  • Kako da generišete setove sličnih komponenata pomoću JavaScript-ovog map()-a.
  • Kako da kreirate nizove filtriranih stavki pomoću JavaScript-ovog filter()-a.
  • Zašto i kako da postavite key za svaku komponentu u kolekciji, kako bi React mogao da prati svaku od njih, čak iako im se pozicija ili podaci promene.

Izazov 1 od 4:
Podeliti listu na dva dela

Ovaj primer prikazuje listu svih ljudi.

Promenite ga da prikazuje dve odvojene liste jednu za drugom: Hemičari i Svi ostali. Kao i ranije, možete odrediti da li je osoba hemičar sledećim uslovom person.profession === 'hemičar'.

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 + ' '}
        poznat je zbog {person.accomplishment}
      </p>
    </li>
  );
  return (
    <article>
      <h1>Naučnici</h1>
      <ul>{listItems}</ul>
    </article>
  );
}