1.組件中用到的一個(gè)變量是不是應(yīng)該作為組件 State拍霜,可以通過下面的三條依據(jù)進(jìn)行判斷:
這個(gè)變量是否是通過Props從父組件中獲戎龆?如果是祠饺,那么它不是一個(gè)狀態(tài)越驻。 這個(gè)變量是否在組件的整個(gè)生命周期中都保持不變?如果是,那么它不是一個(gè)狀態(tài)伐谈。 這個(gè)變量是否可以通過其他狀態(tài)(State)或者屬性(Props)計(jì)算得到烂完?如果是,那么它不是一個(gè)狀態(tài)诵棵。
當(dāng)存在多個(gè)組件共同依賴一個(gè)狀態(tài)時(shí)抠蚣,一般的做法是==狀態(tài)上移==,將這個(gè)狀態(tài)放到這幾個(gè)組件的公共父組件中履澳。
2.State 的更新是異步的
???????調(diào)用setState嘶窄,組件的state并不會(huì)立即改變,setState只是把要修改的狀態(tài)放入一個(gè)隊(duì)列中距贷,React會(huì)優(yōu)化真正的執(zhí)行時(shí)機(jī)柄冲,并且React會(huì)出于性能原因,可能會(huì)將多次setState的狀態(tài)修改合并成一次狀態(tài)修改忠蝗。所以不要依賴當(dāng)前的State现横,計(jì)算下個(gè)State。當(dāng)真正執(zhí)行狀態(tài)修改時(shí)阁最,依賴的this.state并不能保證是最新的State戒祠,因?yàn)镽eact會(huì)把多次State的修改合并成一次,這時(shí)速种,this.state將還是這幾次State修改前的State姜盈。另外需要注意的事,同樣不能依賴當(dāng)前的Props計(jì)算下個(gè)狀態(tài)配阵,因?yàn)镻rops一般也是從父組件的State中獲取馏颂,依然無法確定在組件狀態(tài)更新時(shí)的值。
???????舉個(gè)例子棋傍,對(duì)于一個(gè)電商類應(yīng)用救拉,在我們的購物車中,當(dāng)我們點(diǎn)擊一次購買數(shù)量按鈕舍沙,購買的數(shù)量就會(huì)加1近上,如果我們連續(xù)點(diǎn)擊了兩次按鈕,就會(huì)連續(xù)調(diào)用兩次this.setState({quantity: this.state.quantity + 1})拂铡,在React合并多次修改為一次的情況下,相當(dāng)于等價(jià)執(zhí)行了如下代碼:
Object.assign(
previousState,
{quantity: this.state.quantity + 1},
{quantity: this.state.quantity + 1}
)
???????于是乎葱绒,后面的操作覆蓋掉了前面的操作感帅,最終購買的數(shù)量只增加了1個(gè)。
???????如果你真的有這樣的需求地淀,可以使用另一個(gè)接收一個(gè)函數(shù)作為參數(shù)的setState失球,這個(gè)函數(shù)有兩個(gè)參數(shù),第一個(gè)是當(dāng)前最新狀態(tài)(本次組件狀態(tài)修改后的狀態(tài))的前一個(gè)狀態(tài)preState(本次組件狀態(tài)修改前的狀態(tài)),第二個(gè)參數(shù)是當(dāng)前最新的屬性props实苞。如下所示:
// 正確
this.setState((preState, props) => {
counter: preState.quantity + 1;
})
3.State的更新方式
如有一個(gè)數(shù)組類型的狀態(tài)books豺撑,當(dāng)向books中增加一本書時(shí),使用數(shù)組的concat方法或ES6的數(shù)組擴(kuò)展語法(spread syntax):
在setState中修改數(shù)組時(shí)可以定義新數(shù)組等于state中的數(shù)組然后修改黔牵,也可以用preState方法來代替新數(shù)組
// 方法一:將state先賦值給另外的變量聪轿,然后使用concat創(chuàng)建新數(shù)組
var books = this.state.books;
this.setState({
books: books.concat(['React Guide']);
})
// 方法二:使用preState、concat創(chuàng)建新數(shù)組
this.setState(preState => ({
books: preState.books.concat(['React Guide']);
}))
// 方法三:ES6 spread syntax
this.setState(preState => ({
books: [...preState.books, 'React Guide'];
}))
當(dāng)從books中截取部分元素作為新狀態(tài)時(shí)猾浦,使用數(shù)組的slice方法:
Slice(a,b)用來取數(shù)組中a開始到b-a之間的元素
// 方法一:將state先賦值給另外的變量陆错,然后使用slice創(chuàng)建新數(shù)組
var books = this.state.books;
this.setState({
books: books.slice(1,3);
})
// 方法二:使用preState、slice創(chuàng)建新數(shù)組
this.setState(preState => ({
books: preState.books.slice(1,3);
}))
當(dāng)從books中過濾部分元素后金赦,作為新狀態(tài)時(shí)音瓷,使用數(shù)組的filter方法:
// 方法一:將state先賦值給另外的變量,然后使用filter創(chuàng)建新數(shù)組
var books = this.state.books;
this.setState({
books: books.filter(item => {
return item != 'React';
});
})
// 方法二:使用preState夹抗、filter創(chuàng)建新數(shù)組
this.setState(preState => ({
books: preState.books.filter(item => {
return item != 'React';
});
}))
注意不要使用push绳慎、pop、shift漠烧、unshift偷线、splice等方法修改數(shù)組類型的狀態(tài),因?yàn)檫@些方法都是在原數(shù)組的基礎(chǔ)上修改沽甥,而concat声邦、slice、filter會(huì)返回一個(gè)新的數(shù)組摆舟。
當(dāng)狀態(tài)的類型是普通對(duì)象時(shí)有兩種方法:
1.使用ES6 的Object.assgin方法:
// 方法一:將state先賦值給另外的變量亥曹,然后使用Object.assign創(chuàng)建新對(duì)象
var owner = this.setState.owner;
this.setState({
owner: Object.assign({}, owner, {name: 'Jason'});
})
// 方法二:使用preState、Object.assign創(chuàng)建新對(duì)象
this.setState(preState => ({
owner: Object.assign({}, preState.owner, {name: 'Jason'});
}))
2.使用對(duì)象擴(kuò)展語法(object spread properties)
// 方法一:將state先賦值給另外的變量恨诱,然后使用對(duì)象擴(kuò)展語法創(chuàng)建新對(duì)象
var owner = this.setState.owner;
this.setState({
owner: {...owner, {name: 'Jason'}};
})
// 方法二:使用preState媳瞪、對(duì)象擴(kuò)展語法創(chuàng)建新對(duì)象
this.setState(preState => ({
owner: {...preState.owner, {name: 'Jason'}};
}))
總結(jié)一下,創(chuàng)建新的狀態(tài)對(duì)象的關(guān)鍵是照宝,避免使用會(huì)直接修改原對(duì)象的方法蛇受,而是使用可以返回一個(gè)新對(duì)象的方法。當(dāng)然厕鹃,也可以使用一些Immutable的JS庫兢仰,如Immutable.js,實(shí)現(xiàn)類似的效果剂碴。
那么把将,為什么React推薦組件的狀態(tài)是不可變對(duì)象呢?一方面是因?yàn)椴豢勺儗?duì)象方便管理和調(diào)試忆矛,了解更多可參考這里察蹲;另一方面是出于性能考慮,當(dāng)對(duì)象組件狀態(tài)都是不可變對(duì)象時(shí),我們?cè)诮M件的shouldComponentUpdate方法中洽议,僅需要比較狀態(tài)的引用就可以判斷狀態(tài)是否真的改變宗收,從而避免不必要的render調(diào)用。當(dāng)我們使用React提供的PureComponent時(shí)亚兄,更是要保證組件狀態(tài)是不可變對(duì)象混稽,否則在組件的shouldComponentUpdate方法中,狀態(tài)比較就可能出現(xiàn)錯(cuò)誤儿捧,因?yàn)镻ureComponent執(zhí)行的是淺比較(比較對(duì)象的引用)荚坞。