前言
成都創(chuàng)新互聯(lián)公司服務(wù)項目包括河?xùn)|網(wǎng)站建設(shè)、河?xùn)|網(wǎng)站制作、河?xùn)|網(wǎng)頁制作以及河?xùn)|網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,河?xùn)|網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到河?xùn)|省份的部分城市,未來相信會繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
這篇文章原標(biāo)題是 3 Reasons why I stopped using React.setState ,但是我對原文作者提出的論點不是很感冒,但是作者提出的三點對 React 新手來說是很容易忽略的地方,所以我在這里只提出部分內(nèi)容,而且把標(biāo)題改為 使用React.setState需要注意的三點 。
正文
對 React 新手來說,使用 setState 是一件很復(fù)雜的事情。即使是熟練的 React 開發(fā),也很有可能因為 React 的一些機(jī)制而產(chǎn)生一些bug,比如下面這個例子:
文檔 中也說明了當(dāng)使用 setState 的時候,需要注意什么問題:
注意:
絕對不要 直接改變 this.state ,因為之后調(diào)用 setState() 可能會替換掉你做的改
變。把 this.state 當(dāng)做是不可變的。
setState() 不會立刻改變 this.state ,而是創(chuàng)建一個即將處理的 state 轉(zhuǎn)變。在調(diào)用該方法之后訪問 this.state 可能會返回現(xiàn)有的值。
對 setState 的調(diào)用沒有任何同步性的保證,并且調(diào)用可能會為了性能收益批量執(zhí)行。
setState() 將總是觸發(fā)一次重繪,除非在 shouldComponentUpdate() 中實現(xiàn)了條件渲染邏輯。如果可變對象被使用了,但又不能在 shouldComponentUpdate() 中實現(xiàn)這種邏輯,僅在新 state 和之前的 state 存在差異的時候調(diào)用 setState() 可以避免不必要的重新渲染。
總結(jié)出來,當(dāng)使用 setState 的時候,有三個問題需要注意:
1. setState是異步的(譯者注:不保證同步的)
很多開發(fā)剛開始沒有注意到 setState 是異步的。如果你修改一些 state ,然后直接查看它,你會看到之前的 state 。這是 setState 中最容易出錯的地方。 setState 這個詞看起來并不像是異步的,所以如果你不假思索的用它,可能會造成 bugs 。下面這個例子很好的展示了這個問題:
class Select extends React.Component { constructor(props, context) { super(props, context) this.state = { selection: props.values[0] }; } render() { return (
第一眼看上去,這個代碼似乎沒有什么問題。兩個事件處理中調(diào)用 onSelect 方法。但是,這個 Select 組件中有一個 bug 很好的展現(xiàn)了之前的 GIF 圖。 onSelect 方法永遠(yuǎn)傳遞的是之前的 state.selection 值,因為當(dāng) fireOnSelect 調(diào)用的時候, setState 還沒有完成它的工作。我認(rèn)為 React 至少要把 setState 改名為 scheduleState 或者把回掉函數(shù)設(shè)為必須參數(shù)。
這個bug很容易修改,最難的地方在于你要知道有這個問題。
2. setState會造成不必要的渲染
setState 造成的第二個問題是:每次調(diào)用都會造成重新渲染。很多時候,這些重新渲染是不必要的。你可以用 React performance tools 中的 printWasted 來查看什么時候會發(fā)生不必要渲染。但是,大概的說,不必要的渲染有以下幾個原因:
3.setState并不能很有效的管理所有的組件狀態(tài)
基于上面的最后一條,并不是所有的組件狀態(tài)都應(yīng)該用 setState 來進(jìn)行保存和更新的。復(fù)雜的組件可能會有各種各樣的狀態(tài)需要管理。用 setState 來管理這些狀態(tài)不但會造成很多不需要的重新渲染,也會造成相關(guān)的生命周期鉤子一直被調(diào)用,從而造成很多奇怪的問題。
后話
在原文中作者推薦了一個叫做 MobX 的庫來管理部分狀態(tài),我不是很感冒,所以我就不介紹。如果感興趣的,可以通過最上面的鏈接看看原文中的介紹。
基于上面提出的三點,我認(rèn)為新手應(yīng)該注意的地方是:
setState 是不保證同步的
setState 是不保證同步的,是不保證同步的,是不保證同步的。重要的事情說三遍。之所以不說它是異步的,是因為 setState 在某些情況下也是同步更新的。 可以參考這篇文章
如果需要在 setState 后直接獲取修改后的值,那么有幾個方案:
傳入對應(yīng)的參數(shù),不通過 this.state 獲取
針對于之前的例子,完全可以在調(diào)用 fireOnSelect 的時候,傳入需要的值。而不是在方法中在通過 this.state 來獲取
使用回調(diào)函數(shù)
setState 方法接收一個 function 作為回調(diào)函數(shù)。這個回掉函數(shù)會在 setState 完成以后直接調(diào)用,這樣就可以獲取最新的 state 。對于之前的例子,就可以這樣:
this.setState({ selection: value }, this.fireOnSelect)
使用setTimeout
在 setState 使用 setTimeout 來讓 setState 先完成以后再執(zhí)行里面內(nèi)容。這樣子:
this.setState({ selection: value }); setTimeout(this.fireOnSelect, 0);
直接輸出,回調(diào)函數(shù), setTimeout 對比
componentDidMount(){ this.setState({val: this.state.val + 1}, ()=>{ console.log("In callback " + this.state.val); }); console.log("Direct call " + this.state.val); setTimeout(()=>{ console.log("begin of setTimeout" + this.state.val); this.setState({val: this.state.val + 1}, ()=>{ console.log("setTimeout setState callback " + this.state.val); }); setTimeout(()=>{ console.log("setTimeout of settimeout " + this.state.val); }, 0); console.log("end of setTimeout " + this.state.val); }, 0); }
如果val默認(rèn)為0, 輸入的結(jié)果是:
Direct call 0
In callback 1
begin of setTimeout 1
setTimeout setState callback 2
end of setTimeout 2
setTimeout of settimeout 2
和渲染無關(guān)的狀態(tài)盡量不要放在 state 中來管理
通常 state 中只來管理和渲染有關(guān)的狀態(tài) ,從而保證 setState 改變的狀態(tài)都是和渲染有關(guān)的狀態(tài)。這樣子就可以避免不必要的重復(fù)渲染。其他和渲染無關(guān)的狀態(tài),可以直接以屬性的形式保存在組件中,在需要的時候調(diào)用和改變,不會造成渲染。
避免不必要的修改,當(dāng) state 的值沒有發(fā)生改變的時候,盡量不要使用 setState 。雖然 shouldComponentUpdate 和 PureComponent 可以避免不必要的重復(fù)渲染,但是還是增加了一層 shallowEqual 的調(diào)用,造成多余的浪費。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。