Redux中文文件閱讀總結——快速入門

宮商角徵羽發表於2018-05-15

快速入門

本文為Redux中文文件基礎部分閱讀總結,建議有時間的去詳盡的閱讀一遍 原文

介紹

  1. Redux 是 JavaScript 狀態容器,提供可預測化的狀態管理。

  2. 動機:

    隨著 JavaScript 單頁應用開發日趨複雜,JavaScript 需要管理比任何時候都要多的 state(狀態)。 這些 state 可能包括伺服器響應、快取資料、本地生成尚未持久化到伺服器的資料,也包括 UI 狀態,如啟用的路由,被選中的標籤,是否顯示載入動效或者分頁器等等。管理不斷變化的 state 非常困難。如果一個 model 的變化會引起另一個 model 變化,那麼當 view 變化時,就可能引起對應 model 以及另一個 model 的變化,依次地,可能會引起另一個 view 的變化。

    state 在什麼時候,由於什麼原因,如何變化已然不受控制。

    通過限制更新發生的時間和方式,Redux 試圖讓 state 的變化變得可預測

  3. 核心概念

    用一個普通物件來描述應用的 state,當你想要更新 state 中的資料時,你需要發起一個 action

    Action 就是一個普通 JavaScript 物件(注意到沒,這兒沒有任何魔法?)用來描述發生了什麼 (或者說 Action 是一個指示器,用來指示針對 state 的更新,提出要幹什麼)。

    Reducer 是為了接受 stateaction ,並 返回新的 state 的函式 (或者說 Reducer 是針對 Action 提出的需求,來具體實現這一改動的函式)。

    總體流程就是:state 要改變,發起一個 Action; Action 針對 state 的改變,提出需要幹什麼 ; Reducer 根據 Action 提出的內容,具體去完成這一改變,並且返回新的 state

  4. 三大原則

    Redux 可以用這三個基本原則來描述:

    • 單一資料來源

      整個應用的 state 被儲存在一棵 object tree 中,並且這個 object tree 只存在於 唯一一個 store 中。

    • State 是隻讀的

      唯一改變 state 的方法就是觸發 actionaction 是一個用於描述已發生事件的普通物件。

    • 使用純函式來執行修改

      為了描述 action 如何改變 state tree ,你需要編寫 reducersreducer 只是一些 純函式 ,它接收先前的 stateaction,並返回新的 state

基礎

Action

  1. Action 是把資料從應用(譯者注:這裡之所以不叫 view 是因為這些資料有可能是伺服器響應,使用者輸入或其它非 view 的資料 )傳到 store 的有效載荷。它 是 store 資料的唯一來源 。一般來說你會通過 store.dispatch() 將 action 傳到 store。

    例如,新增新 todo 任務的 action 是這樣的:

    const ADD_TODO = 'ADD_TODO'
    複製程式碼
    {
      type: ADD_TODO,
      text: 'Build my first Redux app'
    }
    複製程式碼

    Action 本質上是 JavaScript 普通物件

    我們約定,action 內必須使用一個字串型別的 type 欄位來表示將要執行的動作。多數情況下,type 會被定義成字串常量。當應用規模越來越大時,建議使用單獨的模組或檔案來存放 action。

    例如:

    import { ADD_TODO, REMOVE_TODO } from '../actionTypes'
    複製程式碼

    樣板檔案使用提醒

    使用單獨的模組或檔案來定義 action type 常量並不是必須的,甚至根本不需要定義。對於小應用來說,使用字串做 action type 更方便些。不過,在大型應用中把它們顯式地定義成常量還是利大於弊的。

    我們應該儘量減少在 action 中傳遞的資料。

Reducer

  1. Reducers 指定了 應用狀態的變化如何響應 actions 併傳送到 store 的,記住 actions 只是描述了有事情發生了這一事實,並沒有描述應用如何更新 state。

    reducer 就是一個純函式,接收舊的 state 和 action,返回新的 state。

    通俗的講:Reducer 就是給 Store 生產新 State 的地方

    (previousState, action) => newState
    複製程式碼

    reducer 是一個純函式,保持 reducer 純淨非常重要。永遠不要在 reducer 裡做這些操作:

    • 修改傳入引數;
    • 執行有副作用的操作,如 API 請求和路由跳轉;
    • 呼叫非純函式,如Date.now()Math.random()

    需要謹記 reducer 一定要保持純淨。

    只要傳入引數相同,返回計算得到的下一個 state 就一定相同。沒有特殊情況、沒有副作用,沒有 API 請求、沒有變數修改,單純執行計算

  2. combineReducers(reducers)

    注意每個 reducer 只負責管理全域性 state 中它負責的一部分。每個 reducer 的 state 引數都不同,分別對應它管理的那部分 state 資料。

    隨著應用的膨脹,我們還可以將拆分後的 reducer 放到不同的檔案中, 以保持其獨立性並用於專門處理不同的資料域。為此,Redux提供了 combineReducers() 工具類,將多個 reducer 管理起來,這樣可以消除一些樣本模板程式碼。

    例如:

    import { combineReducers } from 'redux'
    
    const todoApp = combineReducers({
      visibilityFilter,
      todos
    })
    
    export default todoApp
    複製程式碼

    注意上面的寫法和下面完全等價:

    export default function todoApp(state = {}, action) {
      return {
        visibilityFilter: visibilityFilter(state.visibilityFilter, action),
        todos: todos(state.todos, action)
      }
    }
    複製程式碼

    combineReducers() 所做的只是生成一個函式,這個函式來呼叫你的一系列 reducer,每個 reducer 根據它們的 key 來篩選出 state 中的一部分資料並處理,然後這個生成的函式再將所有 reducer 的結果合併成一個大的物件。如果 combineReducers() 中包含的所有 reducers 都沒有更改 state,那麼也就不會建立一個新的物件。

Store

  1. 我們已經知道了 action 用來描述“發生了什麼”;reducers 根據 action 更新 state 。Store 就是把它們聯絡到一起的物件。

    Store 有以下職責:

    • 維持應用的 state;
    • 提供 getState() 方法獲取 state;
    • 提供 dispatch(action) 方法更新 state;
    • 通過 subscribe(listener) 註冊監聽器;
    • 通過 subscribe(listener) 返回的函式登出監聽器。

    再次強調一下 Redux 應用只有一個單一的 store。當需要拆分資料處理邏輯時,你應該使用 reducer 組合 而不是建立多個 store。

    建立store:

    根據已有的 reducer 來建立 store 是非常容易的。前面我們使用 combineReducers() 將多個 reducer 合併成為一個reducer(todoApp)。現在我們將其匯入,並傳遞 createStore()

    import { createStore } from 'redux'
    import todoApp from './reducers'
    
    let store = createStore(todoApp)
    複製程式碼

資料在 Redux 應用中的流動

  1. 嚴格的單向資料流是 Redux 架構的設計核心。 這意味著應用中所有的資料都遵循相同的生命週期,這樣可以讓應用變得更加可預測且容易理解。

    Redux 應用中資料的生命週期遵循下面 4 個步驟:

    1. 呼叫 store.dispatch(action)

      Action 就是一個描述“發生了什麼”的普通物件。比如:

       { type: 'LIKE_ARTICLE', articleId: 42 }
       { type: 'FETCH_USER_SUCCESS', response: { id: 3, name: 'Mary' } }
       { type: 'ADD_TODO', text: 'Read the Redux docs.' }
      複製程式碼

      你可以在任何地方呼叫 store.dispatch(action),包括元件中、XHR 回撥中、甚至定時器中。

    2. Redux store 呼叫傳入的 reducer 函式

      Store 會把兩個引數傳入 reducer: 當前的 state 樹和 action。例如,在這個 todo 應用中,根 reducer 可能接收這樣的資料:

      // 當前應用的 state(todos 列表和選中的過濾器)
       let previousState = {
         visibleTodoFilter: 'SHOW_ALL',
         todos: [
           {
             text: 'Read the docs.',
             complete: false
           }
         ]
       }
      
       // 將要執行的 action(新增一個 todo)
       let action = {
         type: 'ADD_TODO',
         text: 'Understand the flow.'
       }
      
       // reducer 返回處理後的應用狀態
       let nextState = todoApp(previousState, action)   //當前state,action
      複製程式碼
    3. 根 reducer 應該把多個子 reducer 輸出合併成一個單一的 state 樹

      Redux 原生提供 combineReducers() 輔助函式,來把根 reducer 拆分成多個函式,用於分別處理 state 樹的一個分支。例如:

      function todos(state = [], action) {
         // 省略處理邏輯...
         return nextState
      }
      
      function visibleTodoFilter(state = 'SHOW_ALL', action) {
         // 省略處理邏輯...
         return nextState
      }
      
      let todoApp = combineReducers({     //combineReducers()將多個reducer整合
         todos,
         visibleTodoFilter
      })
      複製程式碼
    4. Redux store 儲存了根 reducer 返回的完整 state 樹

      這個新的樹就是應用的下一個 state!所有訂閱 store.subscribe(listener) 的監聽器都將被呼叫;監聽器裡可以呼叫 store.getState() 獲得當前 state。

      現在,可以應用新的 state 來更新 UI。如果你使用了 React Redux 這類的繫結庫,這時就應該呼叫 component.setState(newState) 來更新。

    5. 總結:

      在寫程式設計中,這個Store一般是見不到它的身影。更多的是和 reducer,state,dispach action打交道。所以 React-Redux 的工作流程 就變成了這樣:

      reducer ----> state ---> store ----> Component ----> UI ----> action ----> dipatch(action) ---> store -----> reducer
      複製程式碼

      由由於 store 不常見,所以可以簡單粗暴理解為:

      reducer ----> state ----> Component ----> UI ----> action ----> dipatch(action) -----> reducer
      複製程式碼

相關文章