Redux概念之二: Redux的三大原則

Redux裏的強硬規則與設計很多,大部份都會與FP(函數式程序開發)、改進本來的Flux架構設計有關。Redux官網文檔上的三大基本原則,主要是由於有可能怕初學者不理解Redux中的一些限制或設計,因此先寫出來講明,這裏面也說明了Redux的設計原理基礎是如何,因此強烈建議全部的初學者必定要完全地理解這三大原則中的意義,多看幾遍,對往後的學習會頗有幫助。如下分別說明,主要以原文的標題與內容說明,盡能夠說明的比較清楚些。html

單一真相來源

你的整個應用中的state(狀態),會存儲在單一個store(存儲)之中的一個對象樹狀結構裏。git

Redux中只有用單一個對象大樹結構來的存儲整個應用的狀態,也就是整個應用中會用到的數據,稱之爲store(存儲)。但要注意的是store(存儲)並非只有單純的數據而已。store就是應用程序領域的狀態,它是類型MVC中的Model(模型的)設計的概念,這設計是由Flux架構而來的,在本來的Flux架構中是容許多個store的結構,Redux簡化爲只有單一個。github

Redux的單一個store的設計有一些好處,對開發者來講,它能夠容易調試與觀察狀態的變化,狀態存儲於對象樹狀結構中,也很容易做到重做/復原(Undo/Redo)的功能。由於只有一個store,但若是store裏要儲放多個不一樣的狀態對象,以及每次的更動數據,天然就會變成了對象樹狀結構(object tree)。redux

此外,若是你想要從store中取出目前的狀態數據,能夠用store的getState()方法。數組

狀態是惟讀的

惟一能更動狀態的是發送一個action(動做),action是一個描述"發生了什麼事"的純對象架構

這裏指的"狀態",是上面說的儲放在store中的狀態數據,你"不能直接"對其中的狀態數據更動,只能"間接"地做這事。這與原先的React中的statesetState的概念有點像,Redux的意思是你不能直接更動store裏面所記錄的狀態值,只能"間接"地透過發送action對象來叫store更動狀態。"間接"地更動狀態是一個很關鍵的設計,這是Flux中單向數據流的重點之一,這對於每一個動做發生,最終會影響到什麼狀態上的更動,一個接一個的順序等等的一種嚴格的設計。函數

你可能會認爲"狀態既然是惟讀",直接與間接有什麼差別,"惟讀"不就表明徹底不能更動,這語句是否是有誤?學習

"惟讀"固然就是徹底不能更動的意思沒錯,因此狀態對象的更動是並非在原先的狀態對象上變更它,而是由原先狀態對象由於動做的加入後,產生一個全新的狀態對象,用這全新的狀態對象來取代原先的狀態對象而已。這在真實世界中或許很難拿比喻來講明,但在軟體世界中這很能夠很容易達成。spa

"發生了什麼事"這句,是表明每一個action都會有一個type(類型),表明這個動做是要做什麼用的,或是如今是發生了什麼,例如是要新建一筆什麼數據,或是刪除整個數據等等,動做對象除了要說明它是要做什麼以外,也須要包含所影響的數據。架構設計

發送一個action(動做),使用的是store.dispatch(action)語法樣式,下面這個例子就是一個要更動狀態的代碼:

store.dispatch({
  type: 'COMPLETE_TODO',
  index: 1
})

中間的那個純對象,就叫做action(動做),它是一個單純用於描述發生了什麼事與相關數據的純對象:

{
  type: 'COMPLETE_TODO',
  index: 1
}

還記得咱們在React中的statesetState方法的設計嗎?state也是不能直接更動的,必定要透過setState方法才能更動它。那這是爲何呢?由於setState不光只是更動state值,它還要做從新渲染的動做,React須要比對目前的狀態,與即將要變更的狀態,這樣才能進移動新渲染的工做。Redux的設計中store是與React中的state相比,它們之間有一些相似的設計。

更動只能由純函數來進行

要指示狀態樹要如何依actions(動做)來做改變,你須要撰寫純粹的概括函數(reducers)

Redux中的reducer的原型會長得像下面這樣,你能夠把它看成就是 以前的狀態 + 動做 = 新的狀態 的公式:

(previousState, action) => newState

注: 你能夠參考Redux中Reducers這一章的內容,裏面有實例。

不過,Redux中的reducer必定是純函數(pure function),也就是不能有反作用的函數。所以由reducer所產生的新狀態,並非直接修改以前的狀態而來,而是一個複製以前狀態,而後加上動做改變的部份,產生的一個新的對象,它這樣設計是有緣由的。

Redux的store設計,並非本來Flux架構中的store,而是ReduceStore,這個ReduceStore是一個在Flux中的store進化版本,在說明中它有一個叫做reduce的方法,說明以下:

reduce(state: T, action: Object): T 概括(Reduces)目前的state(狀態)與一個action(動做)到新的store中的state(狀態)。全部的子類都須要實做這個方法。這個方法必須是純粹而是無反作用。

那爲什麼要用這個進化的ReduceStore?它最後有說明一段:

不須要發送更動事件注意全部繼承自ReduceStore的store,不須要手動發送在reduce()中的更動事件...state(狀態)會自動地比對在每一個dispatch(發送)以前與以後,與自動地做發送更動事件...

ReduceStore的設計與Redux最一開始的版本差很少是同時間發佈的,在開發者之間彼此有交流。Redux的store運用了相似於ReduceStore的設計,因此要更動Redux中的store,須要透過reducer,這是爲了簡化本來在Flux數據流的實做流程。

reducer在Redux中扮演了十分重要的關鍵角色,它是一種對store中所存放的狀態,要如何因應不一樣的動做而進行刷新的函數,而store也是由reducer所建立,例如像下面的代碼:

// @Reducer
//
// action payload = action.text
// 使用純粹函數的數組unshift,不能有反作用
// state(狀態)一開始的值是空數組`state=[]`
function todoApp(state = [], action) {
  switch (action.type) {
    case 'ADD_ITEM':
      return [action.text, ...state]
    default:
      return state
  }
}

// @Store
//
// 由reducer建立store
const store = createStore(todoApp)

針對應用中不一樣功能的狀態,能夠分別寫出不一樣的reducer,Redux中提供了combineReducers函數能夠合併多個reducer,例如如下的代碼:

function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return state.concat([ action.text ])
    default:
      return state
  }
}

function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}

// rootReducer是個組合過的函數,
// 這裏用的是對象屬性初始設置簡寫法,
// combineReducers傳參是一個對象
const rootReducer = combineReducers({
  todos,
  counter
})
相關文章
相關標籤/搜索