你經常會想要從資料集合中顯示多個類似的元件。 你可以使用 JavaScript 陣列方法 來操作資料陣列。 在這個頁面上,你將使用 filter()
和 map()
與 React 來過濾和轉換你的資料陣列成元件陣列。
你將學習
- 如何使用 JavaScript 的
map()
從陣列渲染元件 - 如何使用 JavaScript 的
filter()
只渲染特定元件 - 何時以及為何使用 React key
從陣列渲染資料
假設你有一個內容列表。
<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>
這些列表項之間的唯一區別是它們的內容,它們的資料。 在構建介面時,你經常需要使用不同的資料顯示同一個元件的多個實例:從評論列表到個人資料圖片庫。 在這些情況下,你可以將該資料儲存在 JavaScript 物件和陣列中,並使用方法,例如 map()
和 filter()
來從中渲染元件列表。
以下是如何從陣列生成項目列表的簡短範例
- 將資料移至陣列中
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
成員**映射**到一個新的 JSX 節點陣列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>; }
請注意,上面的沙盒顯示一個控制台錯誤
你將在本頁稍後學習如何修復此錯誤。 在我們開始之前,讓我們為你的資料添加一些結構。
過濾項目陣列
這些資料可以更有結構地組織。
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()
方法只返回這些人。 這個方法接收一個項目陣列,將它們通過一個「測試」(一個返回 true
或 false
的函式),並返回一個只包含通過測試(返回 true
)的項目的新陣列。
你只想要 profession
為 'chemist'
的項目。 這個「測試」函式看起來像 (person) => person.profession === 'chemist'
。 以下是組合方式
- 建立一個只包含「化學家」的新陣列
chemists
,方法是對people
呼叫filter()
並使用person.profession === 'chemist'
進行過濾
const chemists = people.filter(person =>
person.profession === 'chemist'
);
- 現在對
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>
);
- 最後,從你的元件返回
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
保持列表項目的順序
請注意,以上所有沙盒都在主控台中顯示錯誤
您需要為每個陣列項目提供一個 key
—— 一個字串或數字,用於在該陣列的其他項目中唯一標識它
<li key={person.id}>...</li>
鍵值告訴 React 每個組件對應哪個陣列項目,以便稍後可以將它們匹配起來。如果您的陣列項目可以移動(例如,由於排序)、插入或刪除,這一點就變得非常重要。一個精心選擇的 key
可以幫助 React 推斷究竟發生了什麼,並對 DOM 樹進行正確的更新。
您應該將鍵值包含在您的數據中,而不是動態生成它們
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' }];
深入探討
當每個項目需要渲染多個 DOM 節點,而不是一個時,您該怎麼辦?
簡短的 <>...</>
Fragment 語法不允許您傳遞鍵值,因此您需要將它們分組到單個 <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
不同的數據來源提供不同的鍵值來源
- 來自資料庫的數據:如果您的數據來自資料庫,您可以使用資料庫鍵值/ID,它們本質上是唯一的。
- 本地生成的數據:如果您的數據是在本地生成和保存的(例如,筆記應用程式中的筆記),請在創建項目時使用遞增計數器、
crypto.randomUUID()
或像uuid
這樣的套件。
鍵值的規則
- 鍵值在兄弟節點之間必須是唯一的。 但是,在不同陣列中為 JSX 節點使用相同的鍵值是可以的。
- 鍵值不得更改,否則會失去其作用!不要在渲染時生成它們。
為什麼 React 需要鍵值?
想像一下,您桌面上的檔案沒有名稱。相反地,您將按照順序來引用它們——第一個檔案、第二個檔案,依此類推。您可以習慣它,但是一旦您刪除一個檔案,它就會變得混亂。第二個檔案會變成第一個檔案,第三個檔案會變成第二個檔案,依此類推。
資料夾中的檔案名稱和陣列中的 JSX key 具有類似的用途。它們讓我們能夠在其兄弟元素之間唯一地識別一個項目。一個精心選擇的 key 提供比陣列中的位置更多的資訊。即使由於重新排序導致*位置*發生變化,key
讓 React 在其整個生命週期中都能識別該項目。
重點回顧
在本頁中,您學習了
- 如何將資料從組件移出到陣列和物件等資料結構中。
- 如何使用 JavaScript 的
map()
生成多組類似的組件。 - 如何使用 JavaScript 的
filter()
創建過濾後的項目陣列。 - 為什麼以及如何在集合中的每個組件上設置
key
,以便 React 即使在它們的位置或資料發生變化時也能追蹤它們中的每一個。
挑戰 1之 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> ); }