Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `Test`. See https://fb.me/react-warning-keys for more information.
相信在react的使用過程中,大家或多或少都會遇到過這樣的警告,這個警告是提醒開發者,需要對渲染的元件添加key屬性,那麼,這個key屬性的作用到底是什麼呢?
我們先來看一看下面的段執行個體代碼和它的運作效果
import React, {Component} from 'react';
class Test extends Component {
constructor(props) {
super(props);
this.state = {
testArray: [{text: '元件1', id: 'a'}, {text: '元件2', id: 'b'}, {text: '元件3', id: 'c'}, {text: '元件4', id: 'd'}]
}
}
//修改state打亂順序
sort(){
this.setState({
testArray: [{text: '元件1', id: 'a'}, {text: '元件3', id: 'c'}, {text: '元件2', id: 'b'}, {text: '元件4', id: 'd'}]
})
}
render() {
return <div>
<div>不指定key屬性</div>
<ul>
{
this.state.testArray.map((item) => {
return <li ><span>{item.text}</span><input/></li>
})
}
</ul>
<div>指定key屬性</div>
<ul>
{
this.state.testArray.map((item) => {
return <li key={item.id}><span>{item.text}</span><input/></li>
})
}
</ul>
<button onClick={::this.sort}>打亂排序</button>
</div>
}
}
export default Test
代碼很簡單,我們做了一個小試驗,通過同一個數組testArray來渲染兩個不同的清單,一個清單項指定了key屬性,另一個不指定key屬性,然後我們觀察他們打亂前後的運作結果。
打亂順序前,在input中填入内容
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHLyg3Va5WNXFGd5cVWwh2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLwQzM5AzN0ETMwITMwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
打亂順序後
我們可以觀察一下,打亂順序後,有無指定key屬性運作結果的異同。相同的是,每一個項的input中的value都得到了保留,不同的是,如果我們不指定key屬性,清單中元件的标題和input在打亂順序之後,好像已經對不上号了,那麼,是什麼原因造成的呢?
我們來簡單的了解一下react的diff算法政策,我們都知道,react為了提升渲染性能,在内部維持了一個虛拟dom,當渲染結構有所變化的時候,會在虛拟dom中先用diff算法先進行一次對比,将所有的差異化解決之後,再一次性根據虛拟dom的變化,渲染到真實的dom結構中。
而key屬性的使用,則涉及到diff算法中同級節點的對比政策,當我們指定key值時,key值會作為目前元件的id,diff算法會根據這個id來進行比對。如果周遊新的dom結構時,發現元件的id在舊的dom結構中存在,那麼react會認為目前元件隻是位置發生了變化,是以不會将舊的元件銷毀重新建立,隻會改變目前元件的位置,然後再檢查元件的屬性有沒有發生變化,然後選擇保留或修改目前元件的屬性,是以我們可以發現如果我們指定了唯一的key值,如果隻是打亂了資料源,資料源渲染出來的每一個子元件都是整體資料發生變化,而如果不顯式指定key值,結果好像有點出乎我們的意料。
那麼,如果沒有顯式指定key值,會發生什麼事情呢?其實,如果沒有顯式指定,react會把目前元件資料源的index作為預設的key值,那麼,這時候會發生什麼事呢?我們以第二項作為例子,由于我們沒有顯式指定key值,key值會被預設指定為index,也就是1。當我們打亂了資料的順序,資料源的第二項由{text: '元件2', id: 'b'}變成了{text: '元件3', id: 'c'},這時候執行diff算法時,發現key值為1的元件在舊的dom結構中存在,并且元件的位置還是原來的位置,是以,直接保留了原元件,但是元件的标題屬性已經改變了,接着,修改元件的屬性,渲染,于是,我們就看到了,輸入框沒改變,但是标題變了,很顯然,這個結果,有時候并不是我們的本意。而如果我們顯式指定了唯一的key值,依舊以第二項作為例子,執行diff算法時,發現第二項的元件變化了并且新的元件在舊的dom結構中存在,于是将第三項整體移動到第二項,然後檢查屬性有沒有發生變化,渲染,最終出現的結果,就是整體的順序改變了。
是以,在實際開發使用中,我們需要注意什麼呢?
首先,我們要確定key值的唯一,事實上如果key值不唯一的話,react隻會渲染第一個,剩下的react會認為是同一項,直接忽略。其次,盡量避免使用index值作為元件的key值,雖然顯式使用index作為key值可以消除warning,但是,我們舉例出現的情況依舊會出現。