Рэндэрынг спісаў

Часта ўзнікае неабходнасць адлюстравання шэрагу падобных кампанентаў на аснове набору даных. Каб маніпуліраваць масівам даных можна выкарыстоўваць метады масіваў JavaScript. На гэтай старонцы вы будзеце выкарыстоўваць метады filter() і map() разам з React для фільтрацыі і пераўтварэння масіву даных у масіў кампанентаў.

You will learn

  • Як рэндэрыць кампаненты з масіву з дапамогай метаду map()
  • Як рэндэрыць толькі пэўныя кампаненты з дапамогай метаду filter()
  • Калі і навошта выкарыстоўваць ключы ў React

Рэндэрынг даных з масіваў

Дапусцім, у вас ёсць спіс кантэнту.

<ul>
<li>Крэола Кэтрын Джонсан: матэматык</li>
<li>Марыа Хасэ Маліна-Паскель Энрыкес: хімік</li>
<li>Махамад Абдус Салам: фізік</li>
<li>Персі Лавон Джуліан: хімік</li>
<li>Субрахманьян Чандрасекар: астрафізік</li>
</ul>

Адзіная розніца паміж гэтымі пунктамі спісу — іх змест, іх даныя. Пры стварэнні інтэрфейсаў часта трэба паказваць некалькі асобнікаў аднаго і таго ж кампанента, выкарыстоўваючы розныя даныя: ад спісаў каментарыяў да галерэй відарысаў профілю. У такіх сітуацыях вы можаце захоўваць гэтыя даныя ў аб’ектах і масівах JavaScript і выкарыстоўваць такія метады, як map() і filter(), каб рэндэрыць з іх спісы кампанентаў.

Вось кароткі прыклад таго, як стварыць спіс элементаў з масіву:

  1. Перамясціце даныя ў масіў:
const people = [
'Крэола Кэтрын Джонсан: матэматык',
'Марыа Хасэ Маліна-Паскель Энрыкес: хімік',
'Махамад Абдус Салам: фізік',
'Персі Лавон Джуліан: хімік',
'Субрахманьян Чандрасекар: астрафізік'
];
  1. Пераўтварыце элементы масіву people у новы масіў JSX вузлоў — listItems:
const listItems = people.map(person => <li>{person}</li>);
  1. Вярніце listItems з вашага кампанента, абгарнуўшы іх у <ul>:
return <ul>{listItems}</ul>;

Вось што мусіць атрымацца ў выніку:

const people = [
  'Крэола Кэтрын Джонсан: матэматык',
  'Марыа Хасэ Маліна-Паскель Энрыкес: хімік',
  'Махамад Абдус Салам: фізік',
  'Персі Лавон Джуліан: хімік',
  'Субрахманьян Чандрасекар: астрафізік'
];

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

Звярніце ўвагу, што ў кансолі пясочніцы адлюстроўваецца памылка:

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

Вы даведаецеся, як выправіць гэтую памылку пазней на гэтай старонцы. Перш чым перайсці да гэтага, давайце дададзім некаторую структуру да даных.

Фільтрацыя масіваў элементаў

Структуру гэтых даных можна палепшыць.

const people = [{
id: 0,
name: 'Крэола Кэтрын Джонсан',
profession: 'матэматык',
}, {
id: 1,
name: 'Марыа Хасэ Маліна-Паскель Энрыкес',
profession: 'хімік',
}, {
id: 2,
name: 'Махамад Абдус Салам',
profession: 'фізік',
}, {
id: 3,
name: 'Персі Лавон Джуліан',
profession: 'хімік',
}, {
id: 4,
name: 'Субрахманьян Чандрасекар',
profession: 'астрафізік',
}];

Дапусцім, вы хочаце паказаць толькі людзей, чыя прафесія 'хімік'. Каб вярнуць толькі гэтых людзей, вы можаце выкарыстоўваць JavaScript метад filter(). Гэты метад бярэ масіў элементаў, прапускае іх праз «тэст» (функцыю, якая вяртае true або false) і вяртае новы масіў толькі з тых элементаў, якія прайшлі праверку (вярнулі true).

Нам патрэбны толькі элементы, дзе profession з’яўляецца 'хімік'. Функцыя “тэст” для гэтага выглядае так: (person) => person.profession === 'хімік'. Вось як сабраць гэта ўсё разам:

  1. Стварыце новы масіў толькі з «хімікаў» (пераменная chemists) выклікаўшы метад filter() на people для фільтрацыі па person.profession === 'хімік':
const chemists = people.filter(person =>
person.profession === 'хімік'
);
  1. Цяпер пераўтварыце элементы масіву chemists:
const listItems = chemists.map(person =>
<li>
<img
src={getImageUrl(person)}
alt={person.name}
/>
<p>
<b>{person.name}:</b>
{' ' + person.profession}.
Галоўнае дасягненне: {person.accomplishment}
</p>
</li>
);
  1. Нарэшце, вярніце listItems з вашага кампанента:
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 === 'хімік'
  );
  const listItems = chemists.map(person =>
    <li>
      <img
        src={getImageUrl(person)}
        alt={person.name}
      />
      <p>
        <b>{person.name}:</b>
        {' ' + person.profession}.
        Галоўнае дасягненне: {person.accomplishment}
      </p>
    </li>
  );
  return <ul>{listItems}</ul>;
}

Pitfall

Стрэлачныя функцыі няяўна вяртаюць вынік выразу адразу пасля =>, таму не трэба выкарыстоўваць аператар return:

const listItems = chemists.map(person =>
<li>...</li> // Няяўнае вяртанне!
);

Аднак вы павінны яўна напісаць return, калі пасля => ідзе фігурная дужка {!

const listItems = chemists.map(person => { // Фігурная дужка
return <li>...</li>;
});

Стрэлачныя функцыі, якія змяшчаюць => { лічацца функцыямі з «блокавай формай». Яны дазваляюць вам напісаць больш, чым адзін радок кода, але вы павінны напісаць аператар return самастойна. Калі вы забудзецеся гэта зрабіць, функцыя нічога не верне!

Захаванне парадку элементаў спісу з дапамогай key

Звярніце ўвагу, што ўсе пясочніцы вышэй паказваюць памылку ў кансолі:

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

Каб выправіць гэту памылку трэба прысвоіць кожнаму элементу масіву ключ (key) — радок або лік, які адназначна ідэнтыфікуе яго сярод іншых элементаў у гэтым масіве:

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

Note

JSX элементы, што ствараюцца непасрэдна ўнутры выкліку метаду map() заўсёды мусяць мець ключы!

Ключы паведамляюць React, якому элементу масіву адпавядае кожны кампанент, каб потым ён мог іх супаставіць. Гэта важна, калі элементы масіву могуць перамяшчацца (напрыклад, з-за сартавання), устаўляцца або выдаляцца. Добра падабраны key дапамагае React зразумець, што менавіта адбылося, і зрабіць правільныя абнаўленні ў дрэве DOM.

Замест таго, каб генераваць ключы на хаду, вы павінны ўключыць іх у свае даныя:

export const people = [{
  id: 0, // Выкарыстоўваецца як ключ у JSX
  name: 'Крэола Кэтрын Джонсан',
  profession: 'матэматык',
  accomplishment: 'разлікі для касмічных палётаў',
  imageId: 'MK3eW3A'
}, {
  id: 1, // Выкарыстоўваецца як ключ у JSX
  name: 'Марыа Хасэ Маліна-Паскель Энрыкес',
  profession: 'хімік',
  accomplishment: 'адкрыццё арктычнай азонавай дзіркі',
  imageId: 'mynHUSa'
}, {
  id: 2, // Выкарыстоўваецца як ключ у JSX
  name: 'Махамад Абдус Салам',
  profession: 'фізік',
  accomplishment: 'тэорыя электрамагнетызму',
  imageId: 'bE7W1ji'
}, {
  id: 3, // Выкарыстоўваецца як ключ у JSX
  name: 'Персі Лавон Джуліан',
  profession: 'хімік',
  accomplishment: 'піянер у вытворчасці картызону, стэроідаў і супрацьзачаткавых таблетак',
  imageId: 'IOjWm71'
}, {
  id: 4, // Выкарыстоўваецца як ключ у JSX
  name: 'Субрахманьян Чандрасекар',
  profession: 'астрафізік',
  accomplishment: 'разлік масы белага карліка',
  imageId: 'lrWQx8l'
}];

Deep Dive

Адлюстраванне некалькіх вузлоў DOM для кожнага элемента спіса

Што рабіць, калі кожны элемент павінен адлюстроўваць не адзін, а некалькі вузлоў DOM?

Скарочаны сінтаксіс фрагмента <>...</> не дазволіць вам перадаць ключ, таму вам трэба альбо згрупаваць іх у адзін <div>, альбо выкарыстоўваць крыху даўжэйшы і больш выразны сінтаксіс <Fragment>:

import { Fragment } from 'react';

// ...

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

Фрагменты знікаюць з DOM, так што гэта створыць плоскі спіс <h1>, <p>, <h1>, <p> і г.д.

Дзе ўзяць key

Розныя крыніцы даных даюць розныя крыніцы ключоў:

  • Даныя з базы даных: Калі вашы даныя прыходзяць з базы даных, вы можаце выкарыстоўваць ключы/ID з базы даных, якія па сваёй прыродзе з’яўляюцца ўнікальнымі.
  • Лакальна згенераваныя даныя: Калі вашы даныя згенерыраваны і захоўваюцца лакальна (напрыклад, нататкі ў праграме для вядзення нататак), выкарыстоўвайце інкрэментны лічыльнік crypto.randomUUID() або пакет uuid пры стварэнні элементаў.

Правілы ключоў

  • Ключы павінны быць унікальнымі сярод сваіх суседніх элементаў. Аднак, можна выкарыстоўваць аднолькавыя ключы для JSX вузлоў у розных масівах.
  • Ключы не павінны мяняцца паколькі гэта пазбаўляе іх сэнсу! Не генеруйце іх падчас рэндэрынгу.

Навошта React патрэбны ключы?

Уявіце, што файлы на вашым працоўным стале не маюць імёнаў. Замест гэтага вы б спасылаліся на іх па парадку - першы файл, другі файл і г.д. Магчыма да гэтага і можна прызвычаіцца, але калі вы выдаліце які-небудзь файл, парадак зменіцца і ўсё стане заблытаным. Другі файл стане першым файлам, трэці файл будзе другім файлам і г.д.

Імёны файлаў у папцы і JSX ключы ў масіве маюць падобную мэту. Яны дазваляюць нам адрозніваць элементы ад іншых элементаў у масіве. Добра падабраны ключ дае больш інфармацыі, чым пазіцыя ў масіве. Нават калі пазіцыя мяняецца з-за змены парадку, key дазваляе React ідэнтыфікаваць элемент на працягу ўсяго яго існавання.

Pitfall

У вас можа ўзнікнуць спакуса выкарыстоўваць індэкс элемента ў масіве ў якасці ключа. Дарэчы, гэта тое, што React будзе выкарыстоўваць, калі вы не ўкажаце key. Але парадак, у якім вы рэндэрыце элементы, можа памяняцца з часам, калі які-небудзь элемент будзе ўстаўлены, выдалены або калі масіў будзе пераўпарадкаваны. Індэкс у якасці ключа часта прыводзіць да каварных памылак і памылак, што могуць збіваць з толку.

Аналагічна, не генеруйце ключы на ляту, напрыклад, з дапамогай key={Math.random()}. Гэта прывядзе да таго, што ключы ніколі не будуць супадаць паміж рэндэрамі, што прывядзе да перастварэння ўсіх вашых кампанентаў і DOM пры кожным рэндэры. Гэта не толькі павольна, але і прывядзе да страты любых даных уведзеных карыстальнікам унутры элементаў спіса. Замест гэтага выкарыстоўвайце стабільны ідэнтыфікатар, заснаваны на даных.

Звярніце ўвагу, што вашыя кампаненты не атрымаюць key у якасці пропса. Ён выкарыстоўваецца толькі як падказка для React. Калі вашаму кампаненту патрэбны ідэнтыфікатар, вы мусіце перадаць яго як асобны пропс: <Profile key={id} userId={id} />.

Recap

На гэтай старонцы вы даведаліся:

  • Як перамясціць даныя з кампанентаў у такія структуры даных, як масівы і аб’екты.
  • Як стварыць наборы падобных кампанентаў з дапамогай JavaScript метаду map() .
  • Як ствараць масівы адфільтраваных элементаў з дапамогай JavaScript метаду filter().
  • Навошта і як задаваць key для кожнага кампанента ў калекцыі, каб React мог адсочваць кожны з іх, нават калі іх пазіцыя або даныя змяняюцца.

Challenge 1 of 4:
Падзел спісу на два

У гэтым прыкладзе паказаны спіс усіх людзей.

Змяніце яго так, каб паказаць два асобныя спісы адзін за адным: Хімікі і Усе астатнія. Як і раней, вы можаце вызначыць, ці з’яўляецца чалавек хімікам, праверыўшы person.profession === 'хімік'.

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}.
        Галоўнае дасягненне: {person.accomplishment}
      </p>
    </li>
  );
  return (
    <article>
      <h1>Навукоўцы</h1>
      <ul>{listItems}</ul>
    </article>
  );
}