在網(wǎng)上看了很多關(guān)于this.setState()的介紹,覺得受益匪淺,就總結(jié)了一些幫助自己理解的點(diǎn),在此分享出來,如果有侵權(quán)的地方,請(qǐng)及時(shí)提醒。
一、State的定義
狀態(tài)(state) 和 屬性(props) 類似,都是一個(gè)組件所需要的一些數(shù)據(jù)集合,但是它是私有的,并且由組件本身完全控制,可以認(rèn)為它是組件的“私有屬性(或者是局部屬性)”。
二、關(guān)于 setState() 有三件事是你應(yīng)該知道的。# 參考鏈接
1.不要直接修改 state(狀態(tài))
this.state.comment = 'Hello';
上述代碼并不會(huì)重新渲染組件,需要使用this.setState()代替:
this.setState({
comment: 'Hello'
});
需要注意的是唯一可以分配 this.state 的地方是構(gòu)造函數(shù)。
2.state(狀態(tài)) 更新可能是異步的
React 為了優(yōu)化性能,有可能會(huì)將多個(gè) setState() 調(diào)用合并為一次更新。
因?yàn)?code>this.props和this.state 可能是異步更新的,你不能依賴他們的值計(jì)算下一個(gè)state(狀態(tài))。以下面的代碼為例:
this.setState({
counter: this.state.counter + this.props.increment,
});
我們并不能通過上述代碼得到想要的值,為了彌補(bǔ)這個(gè)問題,使用另一種 setState() 的形式,接受一個(gè)函數(shù)。這個(gè)函數(shù)將接收前一個(gè)狀態(tài)作為第一個(gè)參數(shù),應(yīng)用更新時(shí)的 props 作為第二個(gè)參數(shù),代碼如下:
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
3.state(狀態(tài))更新會(huì)被合并
當(dāng)你調(diào)用 setState(), React 將合并你提供的對(duì)象到當(dāng)前的狀態(tài)中。所以當(dāng)State是一個(gè)多鍵值的結(jié)構(gòu)時(shí),可以單獨(dú)更新其中的一個(gè),此時(shí)會(huì)進(jìn)行“差分”更新,不會(huì)影響其他的屬性值。
三、setState()的異步更新。# 原文鏈接
1.執(zhí)行setState()之后干了什么?
setState()方法通過一個(gè)隊(duì)列機(jī)制實(shí)現(xiàn)state更新,當(dāng)執(zhí)行setState()的時(shí)候,會(huì)將需要更新的state合并之后放入狀態(tài)隊(duì)列,而不會(huì)立即更新this.state(可以和瀏覽器的事件隊(duì)列類比)。如果我們不使用setState而是使用this.state.key來修改,將不會(huì)觸發(fā)組件的re-render。如果將this.state賦值給一個(gè)新的對(duì)象引用,那么其他不在對(duì)象上的state將不會(huì)被放入狀態(tài)隊(duì)列中,當(dāng)下次調(diào)用setState()并對(duì)狀態(tài)隊(duì)列進(jìn)行合并時(shí),直接造成了state丟失。
2.setState()可以接受一個(gè)函數(shù)作為參數(shù)?
setState() 不僅能夠接受一個(gè)對(duì)象作為參數(shù),還能夠接受一個(gè)函數(shù)作為參數(shù)。函數(shù)的參數(shù)即為 state 的前一個(gè)狀態(tài)以及 props。
React文檔中對(duì)setState的說明如下:
void setState (
function|object nextState,
[function callback]
)
上述代碼的第二個(gè)參數(shù)是一個(gè)回調(diào)函數(shù),在setState() 的異步操作結(jié)束并且組件已經(jīng)重新渲染的時(shí)候執(zhí)行。換句話說,我們可以通過這個(gè)回調(diào)來拿到更新的state的值。
3.執(zhí)行setState()后能拿到最新的state值嗎?
以前在寫代碼時(shí)候,總是遇到明明執(zhí)行過setState(),但是state的值卻不是最新的,那么如何解決這個(gè)問題呢?
因?yàn)?code>setState()函數(shù)接受兩個(gè)參數(shù),一個(gè)是一個(gè)對(duì)象,就是設(shè)置的狀態(tài),還有一個(gè)是一個(gè)回調(diào)函數(shù),是在設(shè)置狀態(tài)成功之后執(zhí)行的,所以我們可以通過回掉拿到最新的state值。代碼如下:
updateData = (newData) => {
this.setState(
{ data: newData },
() => {
//這里打印的是最新的state值
console.log(this.state.data);
}
);
}
4.setState()一定是異步更新嗎? **# 原文鏈接
我們先來看看下面的代碼:
function incrementMultiple() {
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
}
直觀上來看,當(dāng)上面的 incrementMultiple 函數(shù)被調(diào)用時(shí),組件狀態(tài)的 count 值被增加了3次,每次增加1,那最后 count 被增加了3。但是,實(shí)際上的結(jié)果只給 state 增加了1。
事實(shí)上,setState 方法與包含在其中的執(zhí)行是一個(gè)很復(fù)雜的過程,從 React 最初的版本到現(xiàn)在,也有無數(shù)次的修改。它的工作除了要更動(dòng) this.state 之外,還要負(fù)責(zé)觸發(fā)重新渲染,這里面要經(jīng)過 React 核心 diff 算法,最終才能決定是否要進(jìn)行重渲染,以及如何渲染。而且為了批次與效能的理由,多個(gè) setState 呼叫有可能在執(zhí)行過程中還需要被合并,所以它被設(shè)計(jì)以延時(shí)的來進(jìn)行執(zhí)行是相當(dāng)合理的。

在 React 的 setState 函數(shù)實(shí)現(xiàn)中,會(huì)根據(jù)一個(gè)變量 isBatchingUpdates 判斷是直接更新 this.state 還是放到隊(duì)列中回頭再說,而 isBatchingUpdates 默認(rèn)是 false,也就表示 setState 會(huì)同步更新 this.state,但是,有一個(gè)函數(shù) batchedUpdates,這個(gè)函數(shù)會(huì)把 isBatchingUpdates 修改為 true,而當(dāng) React 在調(diào)用事件處理函數(shù)之前就會(huì)調(diào)用這個(gè) batchedUpdates,造成的后果,就是由 React 控制的事件處理過程 setState 不會(huì)同步更新 this.state。
作者給出了下面的重點(diǎn),趕緊拿起我的小本本記下。
由 React 控制的事件處理過程 setState 不會(huì)同步更新 this.state!
也就是說,在 React 控制之外的情況, setState 會(huì)同步更新 this.state!
在大部分的使用情況下,我們都是使用了 React 庫(kù)中的表單組件,例如 select、input、button 等等,它們都是 React 庫(kù)中人造的組件與事件,是處于 React 庫(kù)的控制之下,比如組件原色 onClick 都是經(jīng)過 React 包裝。在這個(gè)情況下,setState 就會(huì)以異步的方式執(zhí)行。
總結(jié)就到這里了,由于我是技術(shù)渣渣,能看懂和理解的只有這么多了,如果有讀者還想了解更多的內(nèi)容,可以點(diǎn)擊我文中的原文鏈接去看原作者的文章,如果遇到侵權(quán)的地方,請(qǐng)聯(lián)系我,必刪。