iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 19
1
Modern Web

I Want To Know React系列 第 19

I Want To Know React - Key 的常見值 & 最佳實踐

回顧 Key 與 Diff 演算法

在上一個篇章中,我們簡介了 key 在 Virtual DOM diff 演算法中扮演的角色。

我們也了解到 key={index} 在很多狀況下是不好的,因為它會有以下問題:

  • 平均效能較差
  • 可能導致非預期的 render

因此大部分的狀況下應盡量以 key={id} 取代 key={index}

然而讀者這時候也許會冒出一些疑問:

  • 如果資料中沒有一個適合的 id 當作 key 值時怎麼辦呢?
  • 有沒有什麼情形下可以使用 index 當作 key 的值呢?

接下來就來說說 key 的最佳實踐方式來解答以上這些問題吧!

key 常見值 & 最佳實踐

key 的常見值會有幾種,以下將按照推薦程度由高至低排列:

  1. 資料本身的 id
  2. Random id
  3. List index

當然,每種 key 值都有其符合的情境,下面段落將依序介紹這三種 key 常見值。

使用資料本身的 id

第一種,也是最推薦的做法是將資料本身的 id 作為 key 的值使用。

因為資料本身的 id(e.g. 貼文的編號 ...etc)通常會是同屬性資料中的唯一值,因此作為 key 的值非常適合,既不需要擔心 key 會重複,也不需要開發者額外產生 key 專用的 id。

另外,由於資料的 id 與 key 都帶有固定且唯一的概念,所以從易讀性角度來講,把資料 id 當做 key 來講十分直觀好懂。

舉例來說,如果要顯示一組部落格文章,每個文章有其 idtitle。實際資料內容長這樣:

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 值的首選。

使用 Random id

第二種折衷作法,開發者自己產生唯一 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

最後一種,也是較不推薦的作法就是把 list 的 index 當作 key 值使用。

如開頭所講,key={index} 常會發生以下兩個問題:

  • 平均效能較差
  • 可能導致非預期的 render

簡單來講,就是 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 值與對應情境:

  • 資料本身的 id:
    • 資料本身就有 id 時使用
  • Random id:
    • 資料本身沒有 id,但 list 內資料順序有可能改變的狀況
  • List index:
    • 資料本身沒有 id,而 list 內資料順序絕對不會改變的狀況

相信經過這幾個章節,讀者們對 key 的使用方式與運作原理有更進一步的認識了!

參考資料


上一篇
I Want To Know React - Key & Diff 演算法
下一篇
I Want To Know React - 初探 Form & Controlled component
系列文
I Want To Know React30

尚未有邦友留言

立即登入留言