state與狀態(tài)機(jī)思維
狀態(tài)機(jī)的四大概念践惑。
State 苔悦,狀態(tài)。一個狀態(tài)機(jī)至少要包含兩個狀態(tài)水援。例如上面燈泡的例子吮龄,有 燈泡亮和 燈泡滅兩個狀態(tài)盅藻。
Event 癞己,事件淘邻。事件就是執(zhí)行某個操作的觸發(fā)條件或者口令。對于燈泡儿惫,“打開開關(guān)”就是一個事件澡罚。
Action ,動作肾请。事件發(fā)生以后要執(zhí)行動作留搔。例如事件是“打開開關(guān)”,動作是“開燈”铛铁。編程的時候隔显,一個 Action 一般就對應(yīng)一個函數(shù)。
Transition 饵逐,變換括眠。也就是從一個狀態(tài)變化為另一個狀態(tài)。例如“開燈過程”就是一個變換倍权。
React將每一種UI的狀態(tài)都看做為一個簡單的狀態(tài)機(jī)掷豺,那么任意一個UI場景就是狀態(tài)機(jī)中的一種狀態(tài)。
根據(jù)決定狀態(tài)的狀態(tài)機(jī)變量的值薄声,React框架渲染出狀態(tài)機(jī)的當(dāng)前狀態(tài)——對于開發(fā)者來說当船,單個UI場景就被渲染出來了。隨著狀態(tài)機(jī)變量值的變化默辨,UI狀態(tài)機(jī)也在不停的改變狀態(tài)德频,UI場景也隨著不斷渲染。這樣一個過程可以很輕松的做到數(shù)據(jù)與UI保持一致缩幸。
在RN的開發(fā)中壹置,開發(fā)者需要將狀態(tài)機(jī)變量視為“不可變的常量”,在開發(fā)者的代碼中表谊,永遠(yuǎn)不要出現(xiàn) this.state.變量名 = value 這樣的語句钞护。這樣的語句對于JS來講是合法的并且可以被正確執(zhí)行,但是從RN開發(fā)原則的角度說爆办,它是不合法的难咕。當(dāng)開發(fā)者需要改變狀態(tài)機(jī)變量的值時,一定要并且只能使用this.setState函數(shù)押逼。
對于RN開發(fā)者來說步藕,RN開發(fā)的一個重要原則就是:
“努力讓自定義的RN組件成為無狀態(tài)的RN組件”惦界。
或者說在不影響程序結(jié)構(gòu)的情況下挑格,盡可能減少有狀態(tài)的React-native組件的數(shù)量。
那么沾歪,哪些RN組件必須有狀態(tài)機(jī)變量呢漂彤?
當(dāng)一個組件需要處理用戶的輸入,或者需要處理網(wǎng)絡(luò)側(cè)發(fā)給應(yīng)用程序的數(shù)據(jù),或者需要處理超時事件挫望,或者需要處理自己訂閱的事件消息等不可預(yù)知的輸入型事件時立润。它就必須要有對應(yīng)的狀態(tài)機(jī)變量。
努力讓用戶的自定義RN組件成為無狀態(tài)的RN組件媳板,減少狀態(tài)機(jī)變量的數(shù)量桑腮,可以讓代碼的框架更清晰。
一個好的設(shè)計思路是:創(chuàng)建多個只負(fù)責(zé)渲染數(shù)據(jù)的無狀態(tài)React-Native組件蛉幸,將它們封裝在一個有狀態(tài)的RN組件中破讨,并把這個有狀態(tài)的RN組件的狀態(tài)機(jī)變量的值通過props傳給無狀態(tài)的RN組件。這就是React-Redux的設(shè)計原理奕纫。
狀態(tài)機(jī)變量的合并
根據(jù)上面的狀態(tài)機(jī)思維提陶,當(dāng)我們需要重新渲染UI時,只要合并狀態(tài)機(jī)變量即可匹层,常見的合并狀態(tài)機(jī)變量的API有setState和forceUpdate隙笆。
forceUpdate
如果開發(fā)者因為某種原因,使得UI中可變數(shù)據(jù)的來源必須從狀態(tài)機(jī)變量和屬性外獲得升筏,那么就需要這個變量來進(jìn)行界面的強(qiáng)制刷新撑柔。
它會導(dǎo)致不經(jīng)優(yōu)化的全部刷新,即調(diào)用forceUpdate函數(shù)導(dǎo)致的重新渲染過程仰冠,將不會調(diào)用shouldComponentUpdate來檢查并重新渲染乏冀。因此會導(dǎo)致大量無意義的刷新,只要需要UI進(jìn)行立即刷新的特殊情況才需要使用洋只。(它是同步的函數(shù)辆沦,在UI線程上執(zhí)行刷新)
setState
與之不同的是 setState(nextState, callback) 函數(shù)在調(diào)用后并不會進(jìn)行同步的刷新,而是異步的刷新识虚,它需要經(jīng)過優(yōu)化的步驟肢扯,重新計算哪里發(fā)生了改變,哪里需要重新渲染担锤。所以蔚晨,需要指出的是如果setState中的需要合并的新的狀態(tài)機(jī)變量與原本的沒有任何的變化,它將不會進(jìn)行渲染肛循。
同時铭腕,因為setState是異步的,所以如果我們需要setState渲染后的某些屬性來進(jìn)行相應(yīng)的操作多糠,那么就需要使用回調(diào)函數(shù)累舷。例如下面的例子:
如果我們界面中進(jìn)行了控件的產(chǎn)生和銷毀,那么象征該控件的ref只有在被渲染到屏幕上后該組件的ref才存在夹孔,但是這就存在一個問題即setState是異步的被盈,我們執(zhí)行以下的操作就會報ref is undefined的錯誤:
this.setState({xxx:value});
// 特別的析孽,當(dāng)xxx與value變量名重名時,可以省略只怎,即setState({value})與setState({value:value})等價
this.refs.xxx
這是因為setState是異步的操作袜瞬,當(dāng)它被調(diào)用時主線程繼續(xù)向下執(zhí)行,但是這時還沒有渲染完成身堡,即ref為undefined的狀態(tài)邓尤,這時就要采用setState的回調(diào)函數(shù)來完成,它等價于componentDidUpdate贴谎,與componentUpdate相比較好的一點是裁赠,它可以在多個setState中根據(jù)更新的數(shù)據(jù)的不同定制不同的回調(diào)函數(shù),避免componentDidUpdate函數(shù)體過長赴精。
`
最后不會出現(xiàn)問題的調(diào)用方式就是:
this.setState({[key]:value},() => {
this.ref[xxx]
});
shouldComponentUpdate
RN框架使用shouldComponentUpdate函數(shù)來判斷接下來是否進(jìn)行渲染佩捞,這個函數(shù)的原型是 boolean shouldComponentUpdate(nextProps,nextState)。這個函數(shù)會傳入兩個對象蕾哟,分別代表接下來渲染所基于的props與state一忱。
與數(shù)據(jù)庫中的觸發(fā)器很類似,在執(zhí)行shouldComponentUpdate這個函數(shù)時谭确,組件原本的props和state并沒有發(fā)生改變帘营,你還可以用this.state和this.props訪問它。所以可以進(jìn)行當(dāng)前狀態(tài)和更新狀態(tài)的比較來判斷是否需要進(jìn)行重新渲染逐哈。
如果RN組件實現(xiàn)了這個函數(shù)芬迄,那么在執(zhí)行重新渲染時,會先調(diào)用這個函數(shù)昂秃≠魇幔可以在這個函數(shù)中進(jìn)行一定的判斷,如果這個函數(shù)返回false肠骆,那么RN將放棄渲染該組件算途。
借鑒鏈接:https://blog.csdn.net/kkk96291/article/details/110659859