歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 理解Javascript的狀態容器Redux

理解Javascript的狀態容器Redux

日期:2017/3/1 9:06:42   编辑:Linux編程

Redux要解決什麼問題?

隨著 JavaScript 單頁應用開發日趨復雜,JavaScript 需要管理比任何時候都要多的 state (狀態)。 這些 state 可能包括服務器響應、緩存數據、本地生成尚未持久化到服務器的數據,也包括 UI 狀態,如激活的路由,被選中的標簽,是否顯示加載動效或者分頁器等等。
管理不斷變化的 state 非常困難。如果一個 model 的變化會引起另一個 model 變化,那麼當 view 變化時,就可能引起對應 model 以及另一個 model 的變化,依次地,可能會引起另一個 view 的變化。直至你搞不清楚到底發生了什麼。state 在什麼時候,由於什麼原因,如何變化已然不受控制。 當系統變得錯綜復雜的時候,想重現問題或者添加新功能就會變得舉步維艱。
Redux的設計借鑒Flux、 Elm、Immutable,它的出現就是為了解決state裡的數據問題。

Redux是如何工作的?

我們知道,在React中,數據在組件中是單向流動的。數據從一個方向父組件流向子組件(通過props),由於這個特征,兩個非父子關系的組件(或者稱作兄弟組件)之間的通信並不是那麼清楚。
React並不建議直接采用組件到組件的通信方式,盡管它有一些特性可以支持這麼做(比如先將子組件的值傳遞給父組件,然後再由父組件在分發給指定的子組件)。這被很多人認為是糟糕的實踐方式,因為這樣的方式容易出錯而且會讓代碼向“拉面”一樣不容易理解。
當然React也沒有直接建議如何去處理這種情形,以下是React的文檔中關於這部分的描述:

對於非父子關系的組件,你可以自己建立一個全局的事件系統,Flux的模式也是一種可行的方式。

Redux的出現就讓這個問題的解決變得更加方便了。Redux提供一種存儲整個應用狀態到一個地方的解決方案(可以理解為統一狀態層),稱為“store”,組件將狀態的變化轉發通知(dispatch)給store,而不是直接通知其它的組件。組件內部依賴的state的變化情況可以通過訂閱store來實現。
使用Redux,所有的組件都從store裡面獲取它們依賴的state,同時也需要將state的變化告知store。組件不需要關注在這個store裡面其它組件的state的變化情況,Redux讓數據流變得更加簡單。這種思想最初來自Flux,它是一種和React相同的單向數據流的設計模式。

Redux的核心設計理念

Redux有三大原則

  • 單一數據源,整個應用的 state 被儲存在一棵 object tree 中,並且這個 object tree 只存在於唯一一個 store 中
  • State 是只讀的,惟一改變 state 的方法就是觸發 action,action 是一個用於描述已發生事件的普通對象。
  • 使用純函數來執行修改,為了描述 action 如何改變 state tree ,你需要編寫 reducers。

Redux和Flux的主要區別是Redux裡面是單一的數據源設計,而Flux裡面有多個數據源,單一數據源的設計讓React的組件之間的通信更加方便,同時也便於狀態的統一管理。
根據Redux的文檔,狀態變化的唯一方式是觸發一個action(一個可以描述發生了什麼的對象),這意味著我們不能直接的去修改狀態,取而代之的是我們可以通過轉發action去告訴store我們有改變狀態的意圖。store對象提供了非常少的API,僅僅只有4個方法:

store.dispatch(action)
store.subscribe(listener)
store.getState()
replaceReducer(nextReducer)

通過這幾個API不難發現,store並沒有直接提供setState()方法。
另外,由於它大量使用 pure function 和 plain object 等概念(reducer 和 action creator 是 pure function,state 和 action 是 plain object)這對於項目的穩定性會是非常好的保證。

理解Action、Reducer

一個action的例子:

var action = {
  type: 'ADD_USER',
  user: {name: 'Dan'}
};

// 假設store對象已經通過Redux.createStore()創建
store.dispatch(action);

這段代碼中,通過dispatch() 方法將action傳遞給了store。Action 本質上是 JavaScript 普通對象。我們約定,action 內必須使用一個字符串類型的 type 字段來表示將要執行的動作。多數情況下,type 會被定義成字符串常量。當應用規模越來越大時,建議使用單獨的模塊或文件來存放 action。

import { ADD_TODO, REMOVE_TODO } from '../actionTypes'

前面描述過,Redux不允許直接去改變state,必須通過轉發action來告訴store有這個意圖要去改變這個狀態。reducer正是扮演處理轉發過來的action的意圖的函數並且可以改變狀態的角色。一個reducer接受當前的state作為參數,通過返回新的state去改變原有的state:

var someReducer = function(state, action) {
  ...
  return state;
}

由於reducer是純函數,需要注意:

  • 不允許在reducer函數內部進行網絡調用或者數據庫查詢操作
  • 不能改變它的參數
    這樣做的好處是:每次調用同樣的一個函數,傳入相同的值可以得到相同的結果。對系統的其它部分也不會產生副作用。

第一個Redux store

使用Redux.createStore()來創建一個store,並且將所有的reducers作為參數傳遞給它,此處以一個reducer為例子:

var userReducer = function(state=[], action) {
  if (action.type === 'ADD_USER') {
    var newState = state.concat([action.user]);
    return newState;
  }
  return state;
}

// 創建一個store,並且將reducer作為參數傳遞給它
var store = Redux.createStore(userReducer);

// 將action傳遞給store,告訴store我們有改變狀態的意向
store.dispatch({
  type: 'ADD_USER',
  user: {name: 'cpselvis'}
});

上述代碼運行後發生的事情:

  • Store被創建
  • reducer確定初始的state的值是空數組
  • action被轉發給store
  • reducer將newUser添加到state中,並且將它返回,更新store

通過這段代碼可以發現:reducer函數實際上被執行了2次,一次時store創建的時候,一次是action被轉發之後。另外需要注意:Redux希望reducer函數總是返回一個新的狀態。這時的結果:

store.getState();  // => [{name: 'cpselvis'}]

到這裡,component -> action -> store -> reducer -> state 的單向數據流的問題就講完了。概括的說就是:React組件裡面獲取了數據之後(比如ajax請求),然後創建一個action通知store我��這個想改變state的意圖,然後reducers(一個action可能對應多個reducer,可以理解為action為訂閱的主題,可能有多個訂閱者)來處理這個意圖並且返回新的state,接下來store會收集到所有的reducer的state,最後更新state。

Copyright © Linux教程網 All Rights Reserved