【 React 】渲染列表 ( Rendering Lists )

Jamie Lo
8 min readFeb 6, 2023

--

Photo by Justin Owens on Unsplash

本篇筆記摘錄自官方文件 Rendering Lists

在 React 中,當我們想渲染多個相似元件時,可以使用 JavaScript array methods 裡的filter()map()來操縱陣列內的資料。

Rendering data from arrays

以下範例為一個簡單的列表:

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

列表內的每個項目,唯一的不同處是它們的內容,也就是它們的資料。

使用同一個元件來展現不同資料是很常遇到的情況,以之前分享過的 Side Project Good Good Eat POS System 為例,一次顯示多筆菜單與報表資料,就是使用map()實作出來的。

通常我們不一定需要將 API 的所有資料都顯示出來,這時候就是filter()派上用場的時候了。

以下為將列表中項目轉換為陣列的方法:

1. 將資料放進陣列:

const people = [
'Creola Katherine Johnson: mathematician',
'Mario José Molina-Pasquel Henríquez: chemist',
'Mohammad Abdus Salam: physicist',
'Percy Lavon Julian: chemist',
'Subrahmanyan Chandrasekhar: astrophysicist'
];

2. 對people內的成員使用map()map()會 return 一個新陣列listItems

const listItems = people.map(person => <li>{person}</li>);

3. 在元件內 returnlistItems,並使用<ul>包覆:

return <ul>{listItems}</ul>;

完整程式碼如下:

( 將左側拉桿向右移動,可以看見程式碼的部分,拉開後點擊左上角三條線漢堡圖示可以看見完整檔案,右邊則為輸出結果。 )

請注意 console 裡的警告訊息,等等我們會來修復它:

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

Filtering arrays of items

下方的程式碼與上方的是同一個,只是資料內容有了更完整的結構:

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',
}];

以下步驟假定只想篩選出profession'chemist'的人,我們可以使用 JavaScript 中的filter()達成目的,filter()會根據我們給它的條件設定,回傳一個新的陣列,而新陣列的內容,將會只有符合條件設定的項目。

1 . 篩選出profession'chemist'的人:

const chemists = people.filter(person =>
person.profession === 'chemist'
);

2 . 對chemist使用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>
);

3 . 最後,在元件內 returnlistItems

return <ul>{listItems}</ul>;

完整程式碼如下,可以透過右側的輸出顯示看到目前的畫面:

【 注意 】

箭頭函式會 return=>後的程式碼,所以不需要寫出return

const listItems = chemists.map(person =>
<li>...</li> // Implicit return!
);

如果箭頭後面接了大括號{ }則需要加上return

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

Keeping list items in order with key

在剛剛所有的範例裡,都有下面這個警告訊息:

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

我們需要給陣列裡每個項目一個keykey可以是字串或數字,但它必須是獨特的:

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

JSX 元素如果是直接使用map()回傳的資料,通常都會需要設定key

key將會告訴 React,陣列內的項目分別對應到哪一個元件,以利在後續配對它們,這在陣列項目可以移動的情形下是非常重要的 ( 像是插入或刪除 )。

key會幫助 React 推斷發生的事情,並正確的更新 DOM tree。

key的來源應該要從資料取得,而不是自己隨便給它一個。
以這個範例來說,id為每個項目獨特的值,所以我們應該要用它來當key

【 DEEP DIVE 】

Fragment 的簡易語法<>...</>沒有辦法傳遞key,所以需要額外使用<div>包覆,或者是使用顯式的 Fragment 語法<Fragment>

使用此方法必須先從 React 引入 Fragment。

import { Fragment } from 'react';

// ...

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

Fragment 不會產生 DOM 節點,是可以避免產生多餘節點的方法,以上面程式碼為例,因為 Fragment 不會產生節點,最後只會獲得同層級的內容,像是<h1>,<p>,<h1>,<p>

Where to get your key

key的來源大致可以分為兩種:

  • 資料庫內的資料:如果資料來源是資料庫,可以直接使用資料庫裡的 ID,因為是獨特的資訊。
  • 本地產生的資料:如果你的資料來源是在本地產生,可以遞增的計數器crypto.randomUUID(),或是使用像uuid這樣的套件。

Rules of keys

key的規則:

  • 在同一個陣列裡,key必須是獨特的,在不同的陣列裡則可以使用相同的key
  • key不能被改變,不可在渲染時產生key

Why does React need keys?

為什麼 React 會需要key呢?

想像電腦桌面有一堆沒有名字的檔案,我們希望它們依序排好,第一個檔案、第二個檔案…,但在這樣的情形下,如果刪除了第一個檔案,將會對順序產生困惑,原本的第二個檔案將會變成第一個檔案,以此類推。

檔案名稱與key在陣列內的目的是相似的,這讓它們變得有獨特性、有辨識度,一個好的key所能帶來的好處,不僅僅只是位置而已。

假定陣列的順序被重新排序,但key在整個生命週期中始終是獨特且不變的。

【Pitfall】

你可能會想要使用陣列中的索引 ( index ) 來當key
事實上,如果沒有定義key,React 將會使用索引來當key
但是順序是會改變的,陣列有可能會有插入或刪除的操作,或是重新排列,如果將索引設為key,將有可能發生 bug。

同樣地,不可使用 Math.random 當作key的來源,此方法會造成渲染期間key無法順利配對,導致元件與 DOM 不斷重建,請使用穩定的資料 ID。

請注意,元件不會將key視為一個 prop,key只是用來提醒 React 本身,如果元件需要 ID 資料,必須將它們分開傳入:

<Profile key={id} userId={id} />

--

--

Jamie Lo

正在往前端這個知識量爆炸的黑洞前行,內容多為平時的筆記整理,希望也能幫助到同樣在這條道路上前進的人💪