react組件之間的通信

今天來看一下react組件之間是怎么進(jìn)行通訊的癞志。
react推崇的是單向數(shù)據(jù)流今阳,自上而下進(jìn)行數(shù)據(jù)的傳遞,但是由下而上或者不在一條數(shù)據(jù)流上的組件之間的通信就會(huì)變的復(fù)雜妖谴。解決通信問題的方法很多膝舅,如果只是父子級(jí)關(guān)系,父級(jí)可以將一個(gè)回調(diào)函數(shù)當(dāng)作屬性傳遞給子級(jí)技潘,子級(jí)可以直接調(diào)用函數(shù)從而和父級(jí)通信。

組件層級(jí)嵌套到比較深值桩,可以使用上下文getChildContext來傳遞信息奔坟,這樣在不需要將函數(shù)一層層往下傳,任何一層的子級(jí)都可以通過this.context直接訪問滴某。

兄弟關(guān)系的組件之間無法直接通信,它們只能利用同一層的上級(jí)作為中轉(zhuǎn)站幕侠。而如果兄弟組件都是最高層的組件悼潭,為了能夠讓它們進(jìn)行通信舰褪,必須在它們外層再套一層組件,這個(gè)外層的組件起著保存數(shù)據(jù)晃酒,傳遞信息的作用贝次,這其實(shí)就是redux所做的事情。

組件之間的信息還可以通過全局事件來傳遞搁宾。不同頁面可以通過參數(shù)傳遞數(shù)據(jù),下個(gè)頁面可以用location.param來獲取翩腐。其實(shí)react本身很簡單,難的在于如何優(yōu)雅高效的實(shí)現(xiàn)組件之間數(shù)據(jù)的交流等龙。

Redux

首先蛛砰,redux并不是必須的,它的作用相當(dāng)于在頂層組件之上又加了一個(gè)組件位仁,作用是進(jìn)行邏輯運(yùn)算钧嘶、儲(chǔ)存數(shù)據(jù)和實(shí)現(xiàn)組件尤其是頂層組件的通信康辑。如果組件之間的交流不多胸墙,邏輯不復(fù)雜,只是單純的進(jìn)行視圖的渲染础拨,這時(shí)候用回調(diào)校哎,context就行,沒必要用redux抱怔,用了反而影響開發(fā)速度。但是如果組件交流特別頻繁灌危,邏輯很復(fù)雜,那redux的優(yōu)勢就特別明顯了浅蚪。我第一次做react項(xiàng)目的時(shí)候并沒有用redux惜傲,所有的邏輯都是在組件內(nèi)部實(shí)現(xiàn)时甚,當(dāng)時(shí)為了實(shí)現(xiàn)一個(gè)邏輯比較復(fù)雜的購物車,洋洋灑灑居然寫了800多行代碼刀诬,回頭一看我自己都不知道寫的是啥,畫面太感人糠馆。

先簡單說一下redux和react是怎么配合的又碌。react-redux提供了connect和Provider兩個(gè)好基友赠橙,它們一個(gè)將組件與redux關(guān)聯(lián)起來期揪,一個(gè)將store傳給組件。組件通過dispatch發(fā)出action缤苫,store根據(jù)action的type屬性調(diào)用對(duì)應(yīng)的reducer并傳入state和這個(gè)action,reducer對(duì)state進(jìn)行處理并返回一個(gè)新的state放入store镀钓,connect監(jiān)聽到store發(fā)生變化,調(diào)用setState更新組件窟赏,此時(shí)組件的props也就跟著變化。
流程是這個(gè)樣子的:

simple_redux.jpg

值得注意的是connect,Provider,mapStateToProps,mapDispatchToProps是react-redux提供的杯巨,redux本身和react沒有半毛錢關(guān)系,它只是數(shù)據(jù)處理中心仍源,沒有和react產(chǎn)生任何耦合,是react-redux讓它們聯(lián)系在一起嚎于。

接下來具體分析一下,redux以及react-redux到底是怎么實(shí)現(xiàn)的肋僧。

all_redux.png

明顯比第一張要復(fù)雜,其實(shí)兩張圖說的是同一件事覆山。從上而下慢慢分析:

先說說redux:

redux主要由三部分組成:store,reducer魏割,action。

store是一個(gè)對(duì)象遭垛,它有四個(gè)主要的方法:

  • 1、dispatch:

用于action的分發(fā)——在createStore中可以用middleware中間件對(duì)dispatch進(jìn)行改造庶喜,比如當(dāng)action傳入dispatch會(huì)立即觸發(fā)reducer,有些時(shí)候我們不希望它立即觸發(fā)斥扛,而是等待異步操作完成之后再觸發(fā),這時(shí)候用redux-thunk對(duì)dispatch進(jìn)行改造峻村,以前只能傳入一個(gè)對(duì)象,改造完成后可以傳入一個(gè)函數(shù)张肾,在這個(gè)函數(shù)里我們手動(dòng)dispatch一個(gè)action對(duì)象馁启,這個(gè)過程是可控的,就實(shí)現(xiàn)了異步霉颠。

  • 2、subscribe:

監(jiān)聽state的變化——這個(gè)函數(shù)在store調(diào)用dispatch時(shí)會(huì)注冊(cè)一個(gè)listener監(jiān)聽state變化诉位,當(dāng)我們需要知道state是否變化時(shí)可以調(diào)用,它返回一個(gè)函數(shù),調(diào)用這個(gè)返回的函數(shù)可以注銷監(jiān)聽坷衍。 let unsubscribe = store.subscribe(() => {console.log('state發(fā)生了變化')})

  • 3、getState:

獲取store中的state——當(dāng)我們用action觸發(fā)reducer改變了state時(shí),需要再拿到新的state里的數(shù)據(jù)铅协,畢竟數(shù)據(jù)才是我們想要的。getState主要在兩個(gè)地方需要用到骏全,一是在dispatch拿到action后store需要用它來獲取state里的數(shù)據(jù)试吁,并把這個(gè)數(shù)據(jù)傳給reducer,這個(gè)過程是自動(dòng)執(zhí)行的治唤,二是在我們利用subscribe監(jiān)聽到state發(fā)生變化后調(diào)用它來獲取新的state數(shù)據(jù),如果做到這一步缕陕,說明我們已經(jīng)成功了。

  • 4蔬崩、replaceReducer:

替換reducer,改變state修改的邏輯桐罕。
store可以通過createStore()方法創(chuàng)建,接受三個(gè)參數(shù)薪伏,經(jīng)過combineReducers合并的reducer和state的初始狀態(tài)以及改變dispatch的中間件博秫,后兩個(gè)參數(shù)并不是必須的。store的主要作用是將action和reducer聯(lián)系起來并改變state即寒。

  • action:

action是一個(gè)對(duì)象,其中type屬性是必須的凹嘲,同時(shí)可以傳入一些數(shù)據(jù)。action可以用actionCreactor進(jìn)行創(chuàng)造凶朗。dispatch就是把a(bǔ)ction對(duì)象發(fā)送出去。

  • reducer:

reducer是一個(gè)函數(shù)宛畦,它接受一個(gè)state和一個(gè)action,根據(jù)action的type返回一個(gè)新的state。根據(jù)業(yè)務(wù)邏輯可以分為很多個(gè)reducer,然后通過combineReducers將它們合并燃少,state樹中有很多對(duì)象碍遍,每個(gè)state對(duì)象對(duì)應(yīng)一個(gè)reducer怕敬,state對(duì)象的名字可以在合并時(shí)定義。
像這個(gè)樣子:

    const reducer = combineReducers({
         a: doSomethingWithA,
         b: processB,
         c: c
    })
  • combineReducers:

其實(shí)它也是一個(gè)reducer,它接受整個(gè)state和一個(gè)action斋日,然后將整個(gè)state拆分發(fā)送給對(duì)應(yīng)的reducer進(jìn)行處理,所有的reducer會(huì)收到相同的action熬的,不過它們會(huì)根據(jù)action的type進(jìn)行判斷,有這個(gè)type就進(jìn)行處理然后返回新的state橡伞,沒有就返回默認(rèn)值,然后這些分散的state又會(huì)整合在一起返回一個(gè)新的state樹挂脑。

接下來分析一下整體的流程,首先調(diào)用store.dispatch將action作為參數(shù)傳入刁俭,同時(shí)用getState獲取當(dāng)前的狀態(tài)樹state并注冊(cè)subscribe的listener監(jiān)聽state變化牍戚,再調(diào)用combineReducers并將獲取的state和action傳入宪哩。combineReducers會(huì)將傳入的state和action傳給所有reducer,reducer會(huì)根據(jù)state的key值獲取與自己對(duì)應(yīng)的state罗岖,并根據(jù)action的type返回新的state,觸發(fā)state樹的更新哑了,我們調(diào)用subscribe監(jiān)聽到state發(fā)生變化后用getState獲取新的state數(shù)據(jù)。

redux的state和react的state兩者完全沒有關(guān)系,除了名字一樣们镜。

上面分析了redux的主要功能,那么react-redux到底做了什么嚼鹉?

React-Redux

如果只使用redux,那么流程是這樣的:

component --> dispatch(action) --> reducer --> subscribe --> getState --> component

用了react-redux之后流程是這樣的:

component --> actionCreator(data) --> reducer --> component

store的三大功能:dispatch,subscribe酒贬,getState都不需要手動(dòng)來寫了。react-redux幫我們做了這些零如,同時(shí)它提供了兩個(gè)好基友Provider和connect。

Provider是一個(gè)組件肖卧,它接受store作為props,然后通過context往下傳葵姥,這樣react中任何組件都可以通過contex獲取store。也就意味著我們可以在任何一個(gè)組件里利用dispatch(action)來觸發(fā)reducer改變state牡辽,并用subscribe監(jiān)聽state的變化,然后用getState獲取變化后的值奏黑。但是并不推薦這樣做,它會(huì)讓數(shù)據(jù)流變的混亂蹂匹,過度的耦合也會(huì)影響組件的復(fù)用忍啸,維護(hù)起來也更麻煩。

connect --connect(mapStateToProps, mapDispatchToProps, mergeProps, options)是一個(gè)函數(shù)凿滤,它接受四個(gè)參數(shù)并且再返回一個(gè)函數(shù)--wrapWithConnect,wrapWithConnect接受一個(gè)組件作為參數(shù)wrapWithConnect(component),它內(nèi)部定義一個(gè)新組件Connect(容器組件)并將傳入的組件(ui組件)作為Connect的子組件然后return出去恬口。

所以它的完整寫法是這樣的:connect(mapStateToProps, mapDispatchToProps, mergeProps, options)(component)

mapStateToProps(state, [ownProps]):

mapStateToProps 接受兩個(gè)參數(shù),store的state和自定義的props养铸,并返回一個(gè)新的對(duì)象,這個(gè)對(duì)象會(huì)作為props的一部分傳入ui組件。我們可以根據(jù)組件所需要的數(shù)據(jù)自定義返回一個(gè)對(duì)象拯啦。

ownProps的變化也會(huì)觸發(fā)mapStateToProps

function mapStateToProps(state) {
  return { todos: state.todos };
}

** mapDispatchToProps(dispatch, [ownProps]):**

mapDispatchToProps如果是對(duì)象,那么會(huì)和store綁定作為props的一部分傳入ui組件甫匹。如果是個(gè)函數(shù),它接受兩個(gè)參數(shù),bindActionCreators會(huì)將action和dispatch綁定并返回一個(gè)對(duì)象季惯,這個(gè)對(duì)象會(huì)和ownProps一起作為props的一部分傳入ui組件。所以不論mapDispatchToProps是對(duì)象還是函數(shù),它最終都會(huì)返回一個(gè)對(duì)象隐圾,如果是函數(shù),這個(gè)對(duì)象的key值是可以自定義的

function mapDispatchToProps(dispatch) {
   return {
      todoActions: bindActionCreators(todoActionCreators,     dispatch),
      counterActions: bindActionCreators(counterActionCreators, dispatch)
   };
}

mapDispatchToProps返回的對(duì)象其屬性其實(shí)就是一個(gè)個(gè)actionCreator盐碱,因?yàn)橐呀?jīng)和dispatch綁定,所以當(dāng)調(diào)用actionCreator時(shí)會(huì)立即發(fā)送action,而不用手動(dòng)dispatch儒恋。ownProps的變化也會(huì)觸發(fā)mapDispatchToProps。

mergeProps(stateProps, dispatchProps, ownProps):

將mapStateToProps() 與 mapDispatchToProps()返回的對(duì)象和組件自身的props合并成新的props并傳入組件牧嫉。默認(rèn)返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的結(jié)果。
options:

pure = true 表示Connect容器組件將在shouldComponentUpdate中對(duì)store的state和ownProps進(jìn)行淺對(duì)比送淆,判斷是否發(fā)生變化,優(yōu)化性能阐斜。為false則不對(duì)比。
其實(shí)connect函數(shù)并沒有做什么,大部分的邏輯都是在它返回的wrapWithConnect函數(shù)內(nèi)實(shí)現(xiàn)的莉测,確切的說是在wrapWithConnect內(nèi)定義的Connect組件里實(shí)現(xiàn)的。

下面是一個(gè)完整的 react --> redux --> react 流程:

一董朝、Provider組件接受redux的store作為props,然后通過context往下傳。

二遥赚、connect函數(shù)在初始化的時(shí)候會(huì)將mapDispatchToProps對(duì)象綁定到store讲坎,如果mapDispatchToProps是函數(shù)則在Connect組件獲得store后,根據(jù)傳入的store.dispatch和action通過bindActionCreators進(jìn)行綁定瓮栗,再將返回的對(duì)象綁定到store,connect函數(shù)會(huì)返回一個(gè)wrapWithConnect函數(shù)秆撮,同時(shí)wrapWithConnect會(huì)被調(diào)用且傳入一個(gè)ui組件盗蟆,wrapWithConnect內(nèi)部使用class Connect extends Component定義了一個(gè)Connect組件腾供,傳入的ui組件就是Connect的子組件节值,然后Connect組件會(huì)通過context獲得store,并通過store.getState獲得完整的state對(duì)象匿乃,將state傳入mapStateToProps返回stateProps對(duì)象、mapDispatchToProps對(duì)象或mapDispatchToProps函數(shù)會(huì)返回一個(gè)dispatchProps對(duì)象,stateProps岩调、dispatchProps以及Connect組件的props三者通過Object.assign()缰揪,或者mergeProps合并為props傳入ui組件。然后在ComponentDidMount中調(diào)用store.subscribe,注冊(cè)了一個(gè)回調(diào)函數(shù)handleChange監(jiān)聽state的變化毫目。

三、此時(shí)ui組件就可以在props中找到actionCreator刮便,當(dāng)我們調(diào)用actionCreator時(shí)會(huì)自動(dòng)調(diào)用dispatch,在dispatch中會(huì)調(diào)用getState獲取整個(gè)state,同時(shí)注冊(cè)一個(gè)listener監(jiān)聽state的變化入客,store將獲得的state和action傳給combineReducers,combineReducers會(huì)將state依據(jù)state的key值分別傳給子reducer,并將action傳給全部子reducer膀钠,reducer會(huì)被依次執(zhí)行進(jìn)行action.type的判斷融击,如果有則返回一個(gè)新的state,如果沒有則返回默認(rèn)。combineReducers再次將子reducer返回的單個(gè)state進(jìn)行合并成一個(gè)新的完整的state鹅士。此時(shí)state發(fā)生了變化。dispatch在state返回新的值之后會(huì)調(diào)用所有注冊(cè)的listener函數(shù)其中包括handleChange函數(shù)搪泳,handleChange函數(shù)內(nèi)部首先調(diào)用getState獲取新的state值并對(duì)新舊兩個(gè)state進(jìn)行淺對(duì)比奋刽,如果相同直接return,如果不同則調(diào)用mapStateToProps獲取stateProps并將新舊兩個(gè)stateProps進(jìn)行淺對(duì)比狭魂,如果相同,直接return結(jié)束镐牺,不進(jìn)行后續(xù)操作。如果不相同則調(diào)用this.setState()觸發(fā)Connect組件的更新痹束,傳入ui組件,觸發(fā)ui組件的更新,此時(shí)ui組件獲得新的props环壤,react --> redux --> react 的一次流程結(jié)束。

上面的有點(diǎn)復(fù)雜,簡化版的流程是:

一辛友、Provider組件接受redux的store作為props,然后通過context往下傳。

二掖看、connect函數(shù)收到Provider傳出的store,然后接受三個(gè)參數(shù)mapStateToProps,mapDispatchToProps和組件蹲坷,并將state和actionCreator以props傳入組件级乐,這時(shí)組件就可以調(diào)用actionCreator函數(shù)來觸發(fā)reducer函數(shù)返回新的state乞旦,connect監(jiān)聽到state變化調(diào)用setState更新組件并將新的state傳入組件故痊。

connect可以寫的非常簡潔,mapStateToProps,mapDispatchToProps只不過是傳入的回調(diào)函數(shù)甜孤,connect函數(shù)在必要的時(shí)候會(huì)調(diào)用它們囱稽,名字不是固定的流昏,甚至可以不寫名字。

簡化版本:

connect(state => state, action)(Component);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市傻盟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖竿奏,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件平痰,死亡現(xiàn)場離奇詭異,居然都是意外死亡赔蒲,警方通過查閱死者的電腦和手機(jī)母市,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門蒋失,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荆萤,“玉大人邑闲,你說我怎么就攤上這事。” “怎么了嫌褪?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵缨伊,是天一觀的道長。 經(jīng)常有香客問我,道長灾而,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任敛劝,我火速辦了婚禮,結(jié)果婚禮上桩砰,老公的妹妹穿的比我還像新娘。我一直安慰自己煮纵,他們只是感情好套像,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布柳譬。 她就那樣靜靜地躺著蹲姐,像睡著了一般凫岖。 火紅的嫁衣襯著肌膚如雪歼指。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼熄浓。 笑死丁侄,一個(gè)胖子當(dāng)著我的面吹牛石景,可吹牛的內(nèi)容都是我干的筷黔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了眼虱?” 一聲冷哼從身側(cè)響起胆筒,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤彤蔽,失蹤者是張志新(化名)和其女友劉穎镊辕,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體卖哎,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡维贺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年发乔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了只恨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡笛辟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出围来,到底是詐尸還是另有隱情航唆,我是刑警寧澤醇滥,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站窝革,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜厢拭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一楞捂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧涵但,春花似錦塑娇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至嗜桌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工迎吵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留桦他,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親沪曙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子外驱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 做React需要會(huì)什么癌蓖? react的功能其實(shí)很單一,主要負(fù)責(zé)渲染的功能用僧,現(xiàn)有的框架人弓,比如angular是一個(gè)大而...
    蒼都閱讀 14,759評(píng)論 1 139
  • 學(xué)習(xí)必備要點(diǎn): 首先弄明白健芭,Redux在使用React開發(fā)應(yīng)用時(shí),起到什么作用——狀態(tài)集中管理 弄清楚Redux是...
    賀賀v5閱讀 8,896評(píng)論 10 58
  • 前言 本文 有配套視頻伸头,可以酌情觀看。 文中內(nèi)容因各人理解不同,可能會(huì)有所偏差河胎,歡迎朋友們聯(lián)系我討論。 文中所有內(nèi)...
    珍此良辰閱讀 11,904評(píng)論 23 111
  • http://gaearon.github.io/redux/index.html 晌区,文檔在 http://rac...
    jacobbubu閱讀 79,951評(píng)論 35 198
  • 一灾馒、什么情況需要redux? 1容达、用戶的使用方式復(fù)雜 2菇爪、不同身份的用戶有不同的使用方式(比如普通用戶和管...
    初晨的筆記閱讀 2,028評(píng)論 0 11