3.2 Redux
?????????這本書的主題是關(guān)于Redux的毯侦,所以我們不要停留在Flux上太久毡熏。終于砖茸,我們要開始接觸Redux了。
? ? ????我們把Flux看作一個框架理念的話碉碉,Redux是Flux的一種實現(xiàn)柴钻,除了Redux之外,還有很多實現(xiàn)Flux的框架垢粮,比如Reflux贴届、Fluxible等,毫無疑問Redux獲得的關(guān)注最多,這不是偶然的粱腻,因為Redux有很多框架無法比擬的優(yōu)勢庇配。
3.2.1 Redux的基本原則
? ? ? ? 2013年問世的Flux飽受爭議,而2015年Dan Abramov提出了在Flux基礎(chǔ)上的改進框架Redux绍些,則是一鳴驚人捞慌,在所有Flux的變體中算是最受關(guān)注的框架,沒有之一柬批。
? ? ? ? Flux的基本原則是“單項數(shù)據(jù)流”啸澡,Redux在此基礎(chǔ)上強調(diào)三個基本原則:
? ? ? ? ? 唯一數(shù)據(jù)源(Single Source of Truth)
? ? ? ? ? 保持狀態(tài)只讀(State is read-only)
? ? ? ? ? 數(shù)據(jù)改變只能通過純函數(shù)完成(Changes are with pure functions)
? ? ? ? 讓我們逐一介紹這三種基本原則。
????? ? 1.唯一數(shù)據(jù)源
? ? ? ? 唯一數(shù)據(jù)源指的是應用的狀態(tài)數(shù)據(jù)應該只存儲在唯一的一個Store中氮帐。
? ? ? ? 我們已經(jīng)知道嗅虏,在Flux中,應用可以擁有多個Store上沐,往往根據(jù)功能把應用的狀態(tài)數(shù)據(jù)劃分給若干個Store分別存儲管理皮服。比如,在上面的CounterPanel例子中参咙,我們創(chuàng)造了CounterStore和SummaryStore龄广。
? ? ? ? 如果狀態(tài)數(shù)據(jù)分散在多個Store中,容易造成數(shù)據(jù)冗余蕴侧,這樣數(shù)據(jù)一致性方面就會出問題择同。雖然利用Dispatch的waitFor方法可以保證多個Store之間的更新順序,但是這又產(chǎn)生了不同Store之間的顯示依賴關(guān)系净宵,這種依賴關(guān)系的存在增加了應用的復雜度敲才,容易帶來新的問題。
????????Redux對這個問題的解決方法就是择葡,整個應用只保持一個Store紧武,所有組件的數(shù)據(jù)源就是這個Store上的狀態(tài)。
注意:Redux并沒有阻止一個應用擁有多個Store刁岸。只是脏里,在Redux的框架下,糖一個應用用于多個Store不會帶來任何好處虹曙,最后還不如使用一個Store更容易組織代碼迫横。
? ? ????這個唯Store上的狀態(tài),是一個樹形的對象酝碳,每個組件往往只是樹形對象上一部分的數(shù)據(jù)矾踱,而如何設計Store上狀態(tài)的結(jié)構(gòu),就是Redux應用的核心問題疏哗,我們接下來會描述具體細節(jié)呛讲。
2.保持狀態(tài)可讀
? ? ? ? 保持狀態(tài)可讀,就是說不能去修改狀態(tài),要修改Store的狀態(tài)贝搁,必須通過派發(fā)一個action對象完成吗氏,這一點,和Flux并沒有什么區(qū)別雷逆。
? ? ? ? 如果只看這個原則的字面意思弦讽,可能會讓讀者感覺有點費解,還記得那個公式嗎膀哲?UI=render(state)往产,我們已經(jīng)說過驅(qū)動用戶界面更改的是狀態(tài),如果狀態(tài)都是只讀的不能修改某宪,怎么可能引起用戶界面的變化呢仿村?
·? ? ? ? 當然,要驅(qū)動用戶界面渲染兴喂,就要改變應用的狀態(tài)蔼囊,但是改變狀態(tài)的方法不是去修改狀態(tài)上的值,而是創(chuàng)建一個新的狀態(tài)對象返回給Redux瞻想,由Redux完成新的狀態(tài)的組裝压真。
? ? ? ? 這就直接引出了下面的第三個基本原則。
3.數(shù)據(jù)改變只能通過純函數(shù)完成
? ? ? ? 這里所說的純函數(shù)就是Reducer蘑险,Redux這個名字的前三個字母Red代碼的就是Reducer。按照創(chuàng)作者Dan Abramov的說法岳悟,Redux名字的含義是Reducer+Flux佃迄。
? ? ? ? Reducer不是一個Redux特定的術(shù)語,而是一個計算機科學的通過概念贵少,很多語言和框架都有對Reducer函數(shù)的支持呵俏。就以JavaScript為例,數(shù)組類型就有reduce函數(shù)滔灶,接受的參數(shù)就是一個reducer普碎,reduce做的事情就是把數(shù)組所有元素依次做“規(guī)約”,對每個元素都調(diào)用一次參數(shù)reducer录平,通過reducer函數(shù)完成規(guī)約所有元素的功能麻车。
? ? ? ? 下面是一個使用reducer函數(shù)的例子:
? ? ? ? [1,2,3,4].reduce(function reducer(accumulation,item){
????????return accumulation+item
????},0);
? ? ? ? 上面的代碼中,reducer(注意不是reduce)函數(shù)接受兩個參數(shù)斗这,第一個參數(shù)是上一次規(guī)約的結(jié)果动猬,第二個參數(shù)是這一次規(guī)約的元素,函數(shù)體是返回兩者之和表箭,所以這個規(guī)約的結(jié)果就是所有元素之和赁咙。
? ? ? ? 在Redux中,每個reducer的函數(shù)簽名如下所示:
? ? ? ? reducer(state,action)
? ? ? ? 第一個參數(shù)state是當前的狀態(tài),第二個參數(shù)action是接受到的action對象彼水,而reducer函數(shù)要做的事情崔拥,就是根據(jù)state和action的值產(chǎn)生一個新的對象返回,注意reducer必須是純函數(shù)凤覆,也就是說函數(shù)的返回結(jié)果必須完全由參數(shù)state和action決定握童,而且不產(chǎn)生任何副作用,也不能修改參數(shù)state和action對象叛赚。
? ? ? ? 讓我們回顧一下Flux中的Store是如何處理函數(shù)的澡绩,代碼如下:
? ? ? ? CounterStore.dispatchToken = AppDispatcher.register((action)=>{
? ? ? ? ? ? if(action.type ===ActionType.INCREMENT){
? ? ? ? ? ? counterValues[action.counterCaption]++;
? ? ? ? ? ? CounterStore.emitChange();
????????}else if(action.type ===ActionTypes.DECREMENT){
? ????????counterValues[action.counterCaption]--;
? ? ? ? ? ? CounterStore.emitChange();
????????}
????});
? ? ? ? Flux更新狀態(tài)的函數(shù)只有一個參數(shù)action,因為狀態(tài)是由Store直接管理的俺附,所以處理函數(shù)中會看到代碼直接更新state肥卡;在Redux中,一個實現(xiàn)同樣功能的reducer代碼如下:
? ? ? ? function reduce(state,action)=>{
????????const {counterCaption} = action;
? ? ? ? siwtch(action.type){
? ? ? ? ? ? case ActionTypes.INCREMENT:
? ? ? ? ? ? return {...state,[counterCaption]:state[counterCaption]+1};
????????case ActionTypes.DECREMENT:
? ? ? ? ? ? return {...state,[counterCaption]:state[counterCaption]-1};
????????}
????}
????????可以看到reducer函數(shù)不光接受action為參數(shù)事镣,還接受state為參數(shù)步鉴。也就是說,Redux的reducer只負責計算狀態(tài)璃哟,卻并不負責存儲狀態(tài)氛琢。
? ? ? ? 我們會在后面的實例中詳細解釋這個reducer的構(gòu)造。
? ? ? ? 讀到這里随闪,讀者可能會有一個疑問阳似,從Redux的基本原則來看,Redux并沒有賦予我們更強大的功能铐伴,反而是給開發(fā)者增加了很多限制啊撮奏,開發(fā)者喪失了想怎么寫就怎么寫的靈活度。而:增加限制卻正是提高軟件質(zhì)量的法門当宴。
3.2.2 Redux實例
? ? ? ? 單純只看書面介紹難以理解Redux如何工作的畜吊,讓我們還是通過例子來介紹。
? ? ? ? 前面我們用Flux實現(xiàn)了一個ControlPanel的應用户矢,接下來讓我們用Redux來重新實現(xiàn)一遍同樣的功能玲献,通過對比查看二者的差異。
? ? ? ? React和Redux事實上是兩個獨立的產(chǎn)品梯浪,一個應用可以使用React而不使用Redux捌年,也可以使用Redux而不使用React,但是驱证,如果兩者結(jié)合使用延窜,沒有理由不使用一個名叫react-redux的庫,這個庫能夠大大簡化代碼的書寫抹锄。
? ? ? ? 不過逆瑞,如果一開始就使用react-redux荠藤,可能對其設計思路一頭霧水,所以获高,我們的實例先不采用react-redux庫哈肖,而從最簡單的Redux使用方法開始,初步改進念秧,循序漸進地過渡到使用react-redux淤井。
? ? ? ? 最基本的Redux實現(xiàn),存在于本書對于Github的chapter-03/redux_basic目錄中摊趾,在這里我們只關(guān)注使用Redux實現(xiàn)和使用Flux實現(xiàn)的不同的文件币狠。
未完待續(xù)。
? ??????