一,概要
在同系列的上一篇博客React修煉之路(一)裏,我第一次接觸組件key這個概念,因爲不理解key的用處及不清楚React組件的更新機制而遇到父級組件調用了render函數而render內的子組件內容不更新的問題。
在React修煉之路(一)裏我在子組件裏使用了componentWillReceiveProps方法,實現了內容更新,具體內容參見上一篇博客。
最近又看了一些資料,同時實踐得更多,對這個問題有了新的理解,下面簡單說說。
二,學習資料
慣例,先上資料:
核心內容: React是根據組件上設定的key屬性來生成該組件唯一的標識,只有key改變了,React纔會更新組件,否則重用該組件。如果想要更新組件內容,請保證每次的key都不一樣。
建議大家先看第二個文檔:民間文檔:進一步闡述和論證,該文檔有例子演示也有相應的解決方法。解決方法的核心還是這句話:如果想要更新組件內容,請保證當前組件的key與上一狀態組件的key不同。
三,代碼示例
React修煉之路(一)裏面實現組件更新的方法就不說了,這裏說說上面文檔給我的啓發。首先要明確自己的業務需求。
你的組件不是動態組件(不需要動態更新),例如你一個列表只做展示,第一次render出來後就不會改變裏面的內容,那麼你不需要指定key(不指定key時,React也會生成一個默認的標識),或者將index作爲key,只要key不重複即可。
你的組件是動態組件(需要動態更新),例如你一個列表做展示時,能向列表添加或刪除元素,此時組件是需要刷新的,刷新的依據就是key,對於相同的key,React會採取和上一次刷新同樣的方式。這個時候,如果你採取index作爲key,那麼假設你在列表頂部添加了元素,那麼新的元素的key就是index=0,和上一次刷新時舊頂部元素的key相同,此時React將認爲這是這個新元素就是就頂部元素,所以就按照頂部元素的內容刷新該元素,導致新元素的內容就是以前的內容,這個和我的預期不一致,我們預期是頂部元素顯示新的內容。親自試一試。
我希望自己講得足夠清楚,要是不夠清楚的話,千萬要試一試,不過大概實際開發都是遇到問題再去深入學習的吧,實踐越多,遇到的問題越多,就能學到越多。
那麼現在我們都知道了,想要組件動態刷新,只要每次刷新都賦予一個新的獨一無二的key即可,具體做法有很多種,民間文檔:進一步闡述和論證,上面提到的文檔後面就給出了幾種方法,我沒試過,大家感興趣可以去試試。
我的方法,之前查閱資料想了解隨機數的生產原理時,看到這麼一句話:
逝者如斯夫,時間就是一個隨機數。
很有哲理味道的一句話,所以我就採取了時間作爲key,那麼每次生成的key都是當前時間,是獨一無二的,能確保React的動態刷新。代碼如下:
上一篇博客的代碼
render: function(){
var that = this;
return(
<ul>
{
this.state.programs.map(function(program,index) {
return <li key={index} className="channel-program">
<ChannelProgram program_index={index} day={that.props.day} change={that.props.change} bill={program}/>
</li>;
})
}
</ul>
);
}
新的代碼,區別在於將 li 標籤的key由index換成當前時間
render: function(){
var that = this;
return(
<ul>
{
this.state.programs.map(function(program,index) {
return <li key={new Date().getTime()} className="channel-program">
<ChannelProgram program_index={index} day={that.props.day} change={that.props.change} bill={program}/>
</li>;
})
}
</ul>
);
}
乍一看是沒問題的,完美解決問題,但是一運行,結果,你猜。不賣關子了,直接上代碼,正確做法是這樣的。
key={new Date().getTime() + index}
1月14日更新,+new Date() 等同於 new Date().getTime()
key={+new Date() + index}
哈哈,new Date().getTime()返回了一個精確到毫秒的時間,也就是說,如果是毫秒內的,這個返回值是一樣的,也就是key一樣,這是React不允許的,直接報錯。而恰好你的服務器就是那麼快,每個li的生成耗時是毫秒內的,此處的解決方法就是在該基礎上index,這樣生成的key纔是獨一無二的。
對比之前方法的優勢所在
雖然以前寫了下面這個函數能實現動態更新,但是有兩個弊端:
componentWillReceiveProps: function(nextProps,nextState) {
this.setState({
start_time: nextProps.start_time,
end_time: nextProps.end_time,
title: nextProps.title
});
}
一,倒不是代碼量的問題,即使以後this.setState說不定會持續增多,但最主要的還是代碼維護的問題,若採用了以前的方法,每次state的增減都要去維護componentWillReceiveProps這個方法。
二,以前的方法每次都會更新所有的組件,而有時候你不需要更新所有的組件。例如,在增加一項的時候,你並不需要更新其餘項,此時你只需要給新的一樣設置新的獨一無二的key,而其餘項key不變即能實現你的業務邏輯。舉個例子吧。
render: function(){
var that = this;
return(
{
this.state.items.map(function(word) {
return <Object item={word}
key={word.id + ":" + word.order + ":" + (word.color || "")} />;
})
}
);
}
核心內容:
key={word.id + ":" + word.order + ":" + (word.color || "")}
可見,只有id,order,color有改變的時候key纔會改變,這大概就是key的意義所在吧。
四,其他
那天和公司裏面帶我寫CMS的同事聊天,我記住了兩句話:
“我們開始用React也就半年吧,都是現炒現賣。”
“實際工作中現炒現賣會很多,所以學習很重要。”
最近複習周,程序大作業,公司的工作內容,挺忙的,共勉,忙過了會繼續更新實踐中的感悟。