在上一個篇章中,我們簡介了 key
在 Virtual DOM diff 演算法中扮演的角色。
我們也了解到 key={index}
在很多狀況下是不好的,因為它會有以下問題:
因此大部分的狀況下應盡量以 key={id}
取代 key={index}
。
然而讀者這時候也許會冒出一些疑問:
id
當作 key
值時怎麼辦呢?index
當作 key
的值呢?接下來就來說說 key
的最佳實踐方式來解答以上這些問題吧!
key
常見值 & 最佳實踐key
的常見值會有幾種,以下將按照推薦程度由高至低排列:
當然,每種 key
值都有其符合的情境,下面段落將依序介紹這三種 key
常見值。
第一種,也是最推薦的做法是將資料本身的 id 作為 key
的值使用。
因為資料本身的 id(e.g. 貼文的編號 ...etc)通常會是同屬性資料中的唯一值,因此作為 key
的值非常適合,既不需要擔心 key
會重複,也不需要開發者額外產生 key 專用的 id。
另外,由於資料的 id 與 key
都帶有固定且唯一的概念,所以從易讀性角度來講,把資料 id 當做 key
來講十分直觀好懂。
舉例來說,如果要顯示一組部落格文章,每個文章有其 id
與 title
。實際資料內容長這樣:
const posts = [
{ id: 10237488, title: 'I Want To Know React' },
{ id: 10237813, title: 'What is React' },
{ id: 10238976, title: 'What is JSX' }
];
則 key
可以這樣寫:
const postItems = posts.map((post) =>
<li key={post.id}>
{post.title}
</li>
);
如此 postItems
就有渾然天成的 key
值了,不用再怕 sort 或 filter 時會產生不必要的 re-render。
當資料中有 id 時,id 就是 key
值的首選。
第二種折衷作法,開發者自己產生唯一 id 作為 key
的值使用。
這個做法的優點是,Random id 必定為唯一值,因此給 key
使用是絕對沒問題的。
然而缺點是,其易寫性較低。開發者需要花費額外的功才能產生 random id(包括:使用正確的套件、在正確的地方產生 random id ...etc)。
另外其易讀性也稍有打折,因為 key
與資料間沒有關連,且閱讀者有時還需要去尋找 random id 的產生位置。
NPM 上產生 id 的常用套件有以下幾種,他們都可以做到 id 隨機不重複的需求:
讀者可以視需求與當下情境選擇使用哪一套。
讓我們來實際試試看吧。舉例來說,如果要顯示一組 Todo list,然而很不幸的,這個 todo list 資料中只有提供 text
沒有提供 id。實際資料內容長這樣:
const todos = [
{ text: 'Read React document' },
{ text: 'Write article' },
{ text: 'Eat BBQ' }
];
另外,不能把 text
當作 key
來使用,因為無法確保每個的 Todo item text
不會互相重複,因此我們需要先尋找一個適當的地方為每個 Todo item 產生 id
。在這邊使用 uuid 套件示範:
// in data process layer
import { v4 as uuid } from 'uuid';
uuid();
const todosWithId = todos.map((todo) => ({ ...todo, id: uuid() })});
接著我們才可將產生出來的 id
賦值給 key
:
// in component layer
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={todo.id}>
{todo.text}
</li>
);
最後一種,也是較不推薦的作法就是把 list 的 index 當作 key
值使用。
如開頭所講,key={index}
常會發生以下兩個問題:
簡單來講,就是 index
作爲 key
時,只要 list 內的 element 有順序調整的話,就可能非預期的狀況出現。
反過來說,如果確定某項 list 內 element 的順序是絕對不會改動的(沒有 sort / filter / add 刀開頭 ...etc),則可以放心的使用 key={index}
。
舉例剛剛的 Todo list 來說,如果需求保證每個 Todo item 絕對不會改變順序的話,則 key={index}
才會是一個可接受的方案:
const todoItems = todos.map((todo, index) =>
<li key={index}>
{todo.text}
</li>
);
如此 React 就不會跳出沒有 key
的警告,而需求也有被滿足。
然而,若資料本身有 id,或是 list 內順序可能會改動的話,絕對應該選擇前兩種方案。
最後一點需要注意的是,有些開發者會想要把自定義的字串加上 index
做成 key
(e.g. key=${prefix-index}
),然而這與 key={index}
效果完全相同。因為 prefix-index
依然是基於 index
在調整的,prefix-index
不會隨著資料順序改動一起搬移,所以是完全不必要的寫法,使用 key={index}
代替即可。
此章節中,我們介紹了一些常見的 key
值與對應情境:
相信經過這幾個章節,讀者們對 key
的使用方式與運作原理有更進一步的認識了!