你經常會需要用多個相似的 component 來顯示一系列的資料。這個時候你可以在 React 中使用 JavaScript 的 filter()
和 map()
來過濾資料並轉換成包含多個 component 的 array。
You will learn
- 如何使用 JavaScript 的
map()
方法處理 array 資料 render component - 如何使用 JavaScript 的
filter()
方法處理 array 資料 render 特定的 component - 什麼時候使用及為什麼使用 React 中的 key
從 array 中 render 資料
假設你有一個內容列表。
<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>
在這個列表唯一的差異就在於各個內容和資料。當你建立介面時,常常需要使用不同資料顯示相同的 component,從評論列表到個人資料圖庫。在這些情況下,你可以將資料儲存在 JavaScript 的 object 和array 中,並使用 map()
和 filter()
的方法來 render 一個 component 列表。
這裡有個使用 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'
];
- 對
people
成員進行 Map,以建立新的 JSX 節點 array,listItems
:
const listItems = people.map(person => <li>{person}</li>);
- 把
listItems
用<ul>
包起來,並且回傳它:
return <ul>{listItems}</ul>;
來看看運作結果:
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>; }
請注意上面的範例中 sandbox 顯示了一個錯誤訊息:
你將會在這個頁面稍後學到如何修正這個錯誤。在這之前,我們先將這個 array 資料更加結構化。
過濾 array 裡的項目
這讓資料變得更結構化。
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',
}];
假設你想要一種方法,僅顯示其職業為「chemist」的人。你可以使用 JavaScript 的 filter()
方法來篩選符合條件的項目。這個方法會讓 array 中的每個項目進行「過濾」(一個回傳 true
或 false
的函式),最後回傳一個滿足篩選條件項目的新 array。
你只想要 profession
為 'chemist'
的項目。此「測試」函式看起來像 (person) => person.profession === 'chemist'
。以下是如何將它們組合起來的方式:
- 透過在
people
上呼叫filter()
以person.profession === 'chemist'
為過濾條件,建立一個僅包含「化學家」的新chemists
array:
const chemists = people.filter(person =>
person.profession === 'chemist'
);
- 現在對
chemists
進行map()
:
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>
);
- 最後,從你的 component 中回傳
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 === '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>; }
使用 key
保持列表項目的順序
請注意上面所有的範例,所有的 sandbox 都顯示了一個錯誤訊息:
你必須給 array 裡的每一個項目指定一個 key
— 它可以是 string 或是 number,可以在 array 的其他項目中唯一的識別它。
<li key={person.id}>...</li>
這些 key 會告訴 React ,每個 component 對應著 array 裡的哪一項,React 就可以把它們對應起來。這在某些需要被操作的 array 上非常重要,例如排序、新增或是刪除資料等。一個合適的 key 可以幫助 React 推斷發生什麼事,從而正確的更新 DOM Tree。
與其即時產生 key
,你應該將它們包含在你的資料中:
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' }];
Deep Dive
如果你想讓每個列表項目都 render 多個 DOM nodes 而不是一個的話,你該怎麼做?
<>...</>
Fragment 簡短的語法沒有辦法賦予 key 值,所以你只能使用 <div>
將內容包起來,或是使用長一點且更明確的 <Fragment>
語法
import { Fragment } from 'react';
// ...
const listItems = people.map(person =>
<Fragment key={person.id}>
<h1>{person.name}</h1>
<p>{person.bio}</p>
</Fragment>
);
Fragments 不會顯示在 DOM 上,所以這串程式碼換轉換成 <h1>
、<p>
、<h1>
、<p>
等扁平列表。
如何取得你的 key
不同的資料來源提供不同的 keys:
- **來自資料庫的資料:**如果你的資料是來自於資料庫,你可以使用資料庫的 keys/IDs ,它們本身就具有唯一性。
- **本機產生的資料:**如果你資料的產生和儲存都在本機 (例如筆記軟體的筆記),那麼你可使用計數器、
crypto.randomUUID()
或是像uuid
的套件來產生這些 key 值。
Key 的規則
- Siblings 之間的 key 必須是唯一的。 然而,在_不同_的 array 中使用相同的 key,對於 JSX node 是可以的。
- Key 不能改變否則就失去使用它的意義!所以不要在 rendering 時動態產生它們。
為什麼 React 需要 key?
想像你的桌面上沒有檔案名稱。相反的,而是按順序來區分它們 — 第一個檔案、第二個檔案等等。你可能會慢慢習慣,但一旦刪除了檔案,就會變得很混亂。第二個檔案就會變成第一個檔案,第三個檔案就會變成第二個檔案,以此類推。
在資料夾中的檔案名稱和 array 中的 JSX key 有類似的功能。它們讓我們可以在 siblings 之間識別一個項目。一個選擇得當的 key 提供的訊息比array 中的索引來得更好。即使_位置_由於重新排序而改變,key
也可以讓 React 在其生命週期中識別該項目。
Recap
在這篇文章中,你學會了:
- 如何從 component 抽出資料,並把它們放入像是 array 或是 object 結構中。
- 如何使用 JavaScript 的
map
方法來產生一組相似的 component。 - 如何使用 JavaScript 的
filter
方法來過濾 array。 - 為什麼以及如何在集合中的每個 component 設定
key
,使 React 能在資料位置被改變或是發生變化時,能持續追蹤這些 component。
Challenge 1 of 4: 將列表一分為二
此範例顯示所有人的列表。
請試著將列表分為前後兩個列表:分別是化學家與其他科學家。像剛剛學會的一樣,你可以透過 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> ); }