react為什麼不用陣列的下標來繫結key
最近在看一本名叫《深入淺出React和Redux》這一書,裡面談到了react的dom更新比對,記錄一下。
假設有這麼一個元件
<ul>
<ListItem text="first" />
<ListItem text="second" />
<ListItem text="third" />
</ul>
現在,我們在這個元件的前面插入一個新的元件<ListItem text='zero' />
<ul> <ListItem text="zero" /> <ListItem text="first" /> <ListItem text="second" /> </ul>
思考,怎麼更新dom是最優的,react是去怎麼更新?
按照我們的思維,最優的更新dom就是去把新增一個
ListItem
元件,放在第一個。把之前的第一個元件<ListItem text="first" />
以及第二個元件<ListItem text="second" />
往後挪一位。這樣的結果是最好的。
可是react不是這樣更新的!
它先去比較第一個
ListItem
元件,發現元件上的text
值first
變成了zero
,需要更新。第二個元件text
之前的second
變成了first
,也需要更新,最後新建立一個元件,把它的text
設定為second
。react就完成了它的更新。
當然, React 並不是沒有意識到這個問題,所以 React 提供了方法來克服這種浪費,不過需要開發人員在寫程式碼的時候提供一點小小的幫助,這就是 key 的作用。
key的用法
在 React 的眼裡,確定每一個元件在元件序列中的唯一標識就是它的位置,所以
它也完全不懂哪些子元件實際上並沒有改變,為了讓 React 更加“聰明”,就需要開發者
提供一點幫助。
如果在程式碼中明確地告訴 React 每個元件的唯一標識,就可以幫助 React 在處理這個
問題時聰明很多,告訴 React 每個元件“身份證號”的途徑就是 key 屬性。
- 把之前的程式碼加上key
<ul> <ListItem key={1} text="first" /> <ListItem key={2} text="second" /> <ListItem key={3} text="third" /> </ul>
現在再去插入一個ListItem
元件放在最前面,讓它key為0
<ul>
<ListItem key={0} text="zero" />
<ListItem key={1} text="first" />
<ListItem key={2} text="second" />
</ul>
現在,react根據key值,知道了第二個第三個元件是之前的第一個第二個,所以react會建立一個ListItem
元件放在第一位,對於原有的兩個元件只用原有的props觸發更新。這裡就需要元件內部的shouldComponentUpdate
的鉤子函式進行判斷來減少不必要的更新。
但是這個 key 值只是唯一還不足夠,這個 key 值還需要是穩定不變的,試想,如果
key 值雖然能夠在每個時刻都唯一,但是變來變去,那麼就會誤導 React 做出錯誤判斷,
甚至導致錯誤的渲染結果。
<ul>
{
Arr.map((item,index)=>(<ListItem key={index} text={item.text} />))
}
</ul>
用陣列下標作為 key ,看起來 key 值是唯一的,但是卻不是穩定不變的,隨著 Arr
陣列值的不同,同樣一個Listltem
例項在不同的更新過程中在陣列中的下標完全可能不
同,把下標當做 key 就讓 React 徹底亂套了。
需要注意,雖然 key 是一個 prop ,但是接受 key 的元件並不能讀取到 key 的值,因為key
ref
React 保留的兩個特殊 prop ,並沒有預期讓元件直接訪問。