Listojen renderöinti

Saatat usein haluta näyttää useita samanlaisia komponentteja datakokoelmasta. Voit käyttää JavaScriptin listametodeja manipuloidaksesi tietojoukkoja. Tällä sivulla tulet käyttämään filter() ja map() metodeita Reactissa suodattaaksesi ja muuttaaksesi tietojoukon komponenttitaulukoksi.

Tulet oppimaan

  • Miten komponentteja renderöidään listasta käyttäen JavaScriptin map() metodia
  • Miten renderöidään tiettyjä komponentteja käyttäen JavaScriptin filter() metodia
  • Miksi ja milloin käyttää React key:ta

Tietojen renderöinti taulukosta

Sanotaan, että sinulla on taulukko sisältöä.

<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>

Ainoa ero taulukon alkioiden välillä on niiden sisältö eli data. Usein täytyy näyttää useita ilmentymiä samasta komponentista eri datalla käyttöliittymiä rakentaesa: kommenttilistoista profiilikuvien gallerioihin. Näissä tilanteissa voit tallentaa datan JavaScriptin olioihin ja taulukoihin käyttämällä metodeja kuten map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) ja filter() renderöidäksesi niistä komponenttilistan.

Tässä lyhyt esimerkki miten luoda kohdelistaus tietojoukosta:

  1. Siirrä datat taulukkoon:
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. Mäppää people kohteet uuteen taulukkoon listItems koostuen JSX:sta:
const listItems = people.map(person => <li>{person}</li>);
  1. Palauta listItems komponentistasi käärittynä <ul> sisään:
return <ul>{listItems}</ul>;

Tässä lopputulos:

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

Huomaa hiekkalaatikko yllä, joka näyttää konsolivirheen:

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

Opit miten tämä virhe korjataan myöhemmin tällä sivulla. Ennen kuin menemme siihen, jäsennetään dataasi hieman.

Taulukon tietojen suodattaminen

Tätä dataa voidaan jäsentää vielä enemmän.

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',
}, {
name: 'Percy Lavon Julian',
profession: 'chemist',
}, {
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrophysicist',
}];

Sanotaan, että haluat näyttää vain henkilöt joiden ammatti on 'chemist'. Voit käyttää JavaScriptin filter() metodia palauttaaksesi vain ne henkilöt. Tämä metodi ottaa talukon kohteista ja välittää ne “testin” läpi (funktion, joka palauttaa true tai false) ja palauttaa uuden taulukon sisältäen vain ne kohteet jotka läpäisiviät testin (palautti true).

Haluat vain kohteet joissa profession on 'chemist'. Tämä “testaus” funktio tälle näyttäisi seuraavalta (person) => person.profession === 'chemist'. Tässä kuinka se vielä kootaan:

  1. Luo uusi taulukko chemists sisältäen vain “chemist” henkilöt kutsumalla filter() metodia people taulukossa suodattaen person.profession === 'chemist':
const chemists = people.filter(person =>
person.profession === 'chemist'
);
  1. Nyt mäppää 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. Lopuksi, palauta listItems komponentistasi:
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>;
}

Sudenkuoppa

Nuolifunktiot implisiittisesti palauttavat lausekkeen heti => jälkeen, joten et tarvitse return lausetta:

const listItems = chemists.map(person =>
<li>...</li> // Implisiittinen palautus!
);

Kuitenkin, sinun täytyy kirjoittaa return eksplisiittisesti jos => jälkeen tulee { aaltosulje!

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

Nuolifunktiot, jotka sisältävät => { sanotaan omaavan “block body”:n. Niissä voit kirjoittaa enemmän kuin yhden rivin koodia, mutta sinun täytyy kirjoittaa return lauseke. Jos unohdat sen, mitään ei palaudu!

Listojen pitäminen järjestyksessä käyttämällä key:ta

Huomaa, että kaikki alla yllä olevat hiekkalaatikot näyttävät virheen konsolissa:

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

Joudut antamaan jokaiselle taulukon kohteelle key:n — merkkijonon tai numeron joka tunnistaa yksilöllisesti sen muista taulukon kohteista:

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

Huomaa

JSX elementit suoraan map() kutsun sisällä tarvitsevat aina avaimet!

Avaimet kertovat Reactille mitkä taulukon kohteet vastaavat mitäkin komponenttia, jotta se voi yhdistää ne myöhemmin. Tästä tulee tärkeää jos taulukon kohteet voivat liikkua (esim. suodatuksen seurauksena), kohteita lisätään tai poistetaan. Hyvin valittu key auttaa Reactia päättämään mitä oikeastaan on tapahtunut tehdäkseen oikeat päivitykset DOM-puuhun.

Sen sijaan, että avaimet luotaisiin lennosta, kannattaa sisällyttää ne datassasi:

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

Syväsukellus

Useiden DOM-kohteiden näyttäminen jokaikselle taulukon kohteelle

Mitä teet kun yhden kohteen täytyy renderlidä useampi DOM-kohde?

Lyhyt <>...</> Fragment-syntaksi ei anna sinun välittää avainta, joten joudut joko sisällyttämään ne <div>:n sisälle, tai voit käyttää hieman pitempää ja eksplisiittisempää <Fragment> syntaksia:

import { Fragment } from 'react';

// ...

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

Fragmentit häviävät DOM:sta, joten tämä tuottaa tasaisen listauksen elementeistä <h1>, <p>, <h1>, <p>, ja niin edelleen.

Mistä saat key:n

Eri tietolähteet tarjoavat eri lähteet avaimille:

  • Data tietokannasta: Jos datasi tulee tietokannasta, voit käyttää tietokannan avaimia/ID:tä, jotka ovat luonnostaan uniikkeja.
  • Paikallisesti luotu data: Jos datasi on luotu ja tallennettu paikallisesti (esim. muistiinpanot muistiinpanosovelluksessa), käytä kasvavaa laskuria, crypto.randomUUID():ta, tai a pakettia kuten uuid kohteita luodessa.

Avainten säännöt

  • Avainten on oltava uniikkeja sen sisaruksiin nähden. Kuitenkin, on sallittau käyttää samoja avaimia JSX kohteissa eri taulukoissa.
  • Avaimet eivät saa muuttua tai se tuhoaa niiden koko tarkoituksen! Älä luo niitä kun renderöidään.

Miksi React tarvitsee avaimia?

Kuvittele, että tiedostoilla työpöydälläsi ei olisi nimiä. Sen sijaan viittaisit niihin niiden järjestyksen perusteella — ensimmäinen tiedosto, toinen tiedosto, ja niin edelleen. Voisit tottua siihen, mutta kun poistat tiedoston niin siitä tulisi sekavaa. Toisesta tiedostosta tulisi ensimmäinen, kolmannesta toinen, ja niin edelleen.

Tiedostonimet kansioissa ja JSX avaimet taulukossa palvelevat samantapaiseen tarkoitukseen. Sen avulla voidaan yksilöidä kohteet sen sisaruksista. Hyvin valittu avain tarjoaa enemmän tietoa kuin pelkän sijainnin taulukossa. Vaikka sijainti muuttuisi uudelleenjärjestyksen seurauksena, key:n avulla React tunnistaa kohteet sen elinkaaren aikana.

Sudenkuoppa

Saatat tuntea houkutusta käyttää kohteen indeksiä taulukossa sen avaimena. Itse asiassa, sitä React käyttää kun et määrittele key:ta ollenkaan. Kohteiden järjestys muuttuu ajan kuluessa, jos kohteita lisätään, poistetaan tai jos taulukko järjestetään uudelleen. Indeksi avaimena johtaa usein hienovaraisiin ja sekaviin bugeihin.

Vastaavasti, älä luo avaimia lennosta. esim key={Math.random()}. Tämän seurauksena avaimet eivät koskaan vastaa toisiaan renderöintien välillä johtaen jokaisen komponentin ja DOM-kohteen uudelleenluontiin joka kerta. Tämä ei ole ainoastaan hidasta, mutta se myös unohtaa käyttäjän syötteen listan kohteissa. Käytä sen sijaan vakaita ID:ta datan pohjalta.

Huomaa, että komponenttisi eivät vastaanota key:ta propsina. React käyttää sitä vinkkinä vain itselleen. Jos komponenttisi tarvitsee ID:n, täytyy se välittää erillisenä propsina: <Profile key={id} userId={id} />.

Kertaus

Tällä sivulla olet oppinut:

  • Miten siirtää dataa komponenteista ulos tietorakenteisiin kuten taulukoihin ja olioihin.
  • Miten luodaan joukkoja samanlaisia komponentteja käyttämällä JavaScript:n map() metodia.
  • Miten luodaan taulukkoja suodatetuista kohteista käyttämällä JavaScript:n filter() metodia.
  • Miksi ja miten asetetaan key jokaisen kokoelman komponentille, jotta React voi pitää kohteita yllä vaikka niiden sijainti tai data muuttuisi.

Haaste 1 / 4:
Listan jakaminen kahteen

Tämä esimerkki näyttää listan kaikista henkilöistä.

Muuta se näyttämään kaksi erillistä listaa toinen toisensa jälkeen: Chemists ja Everyone Else. Kuten aiemmin, voit määritellä mikäli henkilö on kemisti tarkistamalla jos 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>
  );
}