1. 程式人生 > 實用技巧 >react為什麼不用陣列的下標來繫結key

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元件,發現元件上的textfirst變成了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 ,並沒有預期讓元件直接訪問。