Redux和React-Redux的實現(三):中間件的原理和applyMiddleware、Thunk的實現

来源:https://www.cnblogs.com/isLiu/archive/2017/12/31/8157463.html
-Advertisement-
Play Games

現在我們的Redux和React Redux已經基本實現了,在Redux中,觸發一個action,reducer立即就能算出相應的state,如果我要過一會才讓reducer計算state呢怎麼辦?也就是我們如何實現非同步的action呢?這裡就要用到中間件(middleware) 1. 中間件(mi ...


現在我們的Redux和React-Redux已經基本實現了,在Redux中,觸發一個action,reducer立即就能算出相應的state,如果我要過一會才讓reducer計算state呢怎麼辦?也就是我們如何實現非同步的action呢?這裡就要用到中間件(middleware)

1. 中間件(middleware)介紹


中間就是在action與reducer之間又加了一層,沒有中間件的Redux的過程是:action -> reducer,而有了中間件的過程就是action -> middleware -> reducer,使用中間件我們可以對action也就是對dispatch方法進行裝飾,我們可以用它來實現非同步action、列印日誌、錯誤報告等功能。

又是裝飾器,沒錯,這塊的好多東西都離不開裝飾器模式,所以,設計模式很重要。

關於中間件,有很多框架或者是類庫都使用了中間件,像express、koa、mongoose等都有使用。

2. Redux中間件的使用


我們可以使用Redux提供的applyMiddleware方法來使用一個或者是多個中間件,將它作為createStore的第二個參數傳入即可,我們以Redux-Thunk為例

import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

const store = createStore(counter, applyMiddleware(thunk))
ReactDOM.render(
  (
    <Provider store={store}>
      <App />
    </Provider>
  ),
  document.getElementById('root')
)

通過thunk中間件,我們就可以實現非同步的action了。

export function addAsync(){
  return dispatch => {
    setTimeout(() => {
      dispatch(add())
    }, 2000);
  }
}

想要實現中間件,我們首先有兩個任務要做:

  1. 擴展createStore方法,使它可以接收第二個參數。

  2. applyMiddleware方法的實現。

3. createStore方法的擴展


我們在createStore中加入第二個參數enhancer, 專業的解釋應該叫增強器,叫middleware也可以的。

我們已經說過中間件的作用就是通過改變dispatch方法來改變數據流,所以我們這裡直接用enhancer對createStore方法進行裝飾。Redux的源碼也是這麼寫的,哈哈哈哈,怎麼和我想到的一模一樣呢?因為我看了Redux的源碼。。

export function createStore (reducer,enhancer) {
  if (enhancer) {
    return enhancer(createStore)(reducer)
  }
  let state = {}
  let listeners = []

  function getState () {
    return state
  }
  function subscribe (listener) {
    listeners.push(listener)
  }
  function dispatch (action) {
    state = reducer(state, action)
    listeners.forEach(listener => listener())
    return action
  }

  dispatch({type: '@myRedux'})
  return {getState, subscribe, dispatch}
}

高階函數的寫法,應該都能看懂了吧?前幾篇隨筆有詳細的講高階函數,還有例子。

4.applyMiddleware方法的實現


先看我們上邊對enhancer的調用,enhancer也就是我們的applyMiddleware接受了createStore做參數,返回了一個函數,這個函數的參數是reducer。現在我們對這種兩層嵌套的函數已經不陌生了,其實它就是一個return兩層的函數。

我們的applyMiddleware主要做了什麼呢?首先通過傳入的createStore方法create了一個store,然後將store的dispatch傳遞給middleware,由middleware對dispatch進行包裝,返回一個帶有被包裝的dispatch的store。

看到這裡,很簡單嘛。但是註意,還記得我們是怎麼使用非同步的action的嗎?

export function addAsync(){
  return (dispatch, getState) => {
    setTimeout(() => {
      dispatch(add())
    }, 2000);
  }
}

居然還可以在可以在非同步的action中拿到dispatch和getState方法,所以要對這個進行處理,也不是很難,把他倆傳給我們的middle就好了。

都說到這裡了,能不能自己寫出來呢?

export function applyMiddleware (middleware){
    return createStore => (...args) => {
        const store = createStore(...args)
        let dispatch = store.dispatch

        const midApi = {
            getState: store.getState,
            dispatch: (...args)=>dispatch(...args)
        }
        dispatch = middleware(midApi)(store.dispatch)
        return {
            ...store,
            dispatch
        }
    }
}

如果我們執行了被包裝後的dispatch,就相當於執行了middleware(midApi)(store.dispatch)(action)這段語句,這是一個三層的嵌套函數,我們也稱作柯里化。

5.自己的redux-thunk


其實自己的thunk很簡單,正常的action的的返回值是個對象,前面已經說過,非同步的action的返回值是一個函數,那麼我們只需要判斷一下action的返回的類型即可。

const thunk = ({dispatch, getState}) => next => action => {
    if (typeof action === 'function') {
        return action(dispatch, getState)
    }
    return next(action)
}

export thunk

在這裡呢,dispatch和getState就是我們在applyMiddleware中傳入的那個midApi對象,next就是store.dispatch也可以理解為下一個中間件,如果action的類型是object,說明這是一個同步的,直接dispatch就好了,如果
action的類型是function,當觸發這個dispatch的時候,就觸發action這個函數,同時將dispatch和getState方法傳入到action函數中,這也是為什麼我們能在非同步action中拿到dispatch和getState方法的原因。

6.多個中間件合併與compose方法


我們的applyMiddle方法還不是太完善,只能使用一個中間件,使用多個中間件怎麼辦,這個,簡單,map一下唄。如果是要求多個中間件依此執行怎麼辦?還是map呀,好,來map一下。

我們會得到這樣的代碼:

const store = createStore(
    reducer,
    applyMiddleware(middlewareOne) (
        middlewareTwo(
          middlewareThree(
              ...
          )
        )
    )
)

我們會發現,我們陷入了一個深度嵌套的函數當中,這時我們就需要一個compose方法來結合一下,方便我們的書寫。

compose是函數式編程的一種寫法,compose的作用是從右到左結合多個函數,形成一個最終函數。就是將fn1(fn2(fn3()))的形式,變成compose(fn1, fn2, fn3)的形式。

compose 做的只是讓你在寫深度嵌套的函數時,避免了代碼的向右偏移。不要覺得它很複雜。

compose方法的實現:

export function compose (...funcs){
    if (funcs.length==0) {
        return arg=>arg
    }
    if (funcs.length==1) {
        return funcs[0]
    }
    return funcs.reduce((ret,item)=> (...args)=>{
                console.log(ret)
       return ret(item(...args))
      })
}

compose不是那麼複雜,關於如果想瞭解更多關於compose的知識,可以看看Redux對compose的說明

到這裡我們可以使用多個中間件的applyMiddleware方法已經實現了,整個的applyMiddleware方法在這裡:

export function applyMiddleware (...middlewares){
    return createStore=>(...args)=>{
        const store = createStore(...args)
        let dispatch = store.dispatch

        const midApi = {
            getState:store.getState,
            dispatch:(...args)=>dispatch(...args)
        }
        const middlewareChain = middlewares.map(middleware=>{
            return middleware(midApi)
        })
        console.log(compose(...middlewareChain)(store.dispatch))
        dispatch = compose(...middlewareChain)(store.dispatch)
        return {
            ...store,
            dispatch
        }
    }
}
export function compose(...funcs){
    if (funcs.length==0) {
        return arg=>arg
    }
    if (funcs.length==1) {
        return funcs[0]
    }
    return funcs.reduce((ret,item)=> (...args)=>{
                console.log(ret)
       return ret(item(...args))
      })
}

到這裡,整個Redux和React-Redux的基本原理我們已經清楚了,也已經基本實現了,發現其中涉及到很多函數式編程和裝飾者模式,還有一次觀察者模式,所以,編程思想和設計模式是很重要的,有時間一定要加強這方面的學習。

我們現在有了這些基礎,可以去看看Redux和React-Redux的源碼,也大體上和我寫的是差不多的,因為我也看了源碼。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、Shortcut 簡介 Shortcut 是 Android 7.1 (API Level 25) 的新特性,類似於蘋果的 3D Touch ,但並不是壓力感應,只是一種長按菜單。Shortcut 是受啟動器限制的,也就是說國內大廠的定製系統大多數是不支持的,那些所謂的可以 pin 在桌面上的應 ...
  • 手機拍照身份證識別使用成熟的OCR文字識別技術,通過手機或者帶有攝像頭的終端設備對身份證拍照,並對證件照片做OCR文字識別,提取身份證信息。此技術越來越被廣大消費用戶認知並使用。優點是:方案成本低,用於智能手機,使用環境方便,功能容易擴展。 近幾年,各種各樣的APP正在取代PC端軟體成為用戶應用方式 ...
  • 13.Label的作用是什麼?是怎麼用的? label標簽來定義表單控制間的關係,當用戶選擇該標簽時,瀏覽器會自動將焦點轉到和標簽相關的表單事件上。 <label for="Name">Number:</label> <input type="text" name="Name" id="Name"/ ...
  • 1. css的全稱 2. CSS的層疊次序:優先順序由低到高 ·瀏覽器設置 ·外部樣式表 或者 內部樣式表 —— 就近原則 ·內聯樣式 3. CSS的3種形式,以及每種形式的語法格式 ——註意樣式表的為什麼常用link 4. CSS基本語法: selector(property:value; colo ...
  • opacity用來設置元素的透明度。 值被約束在[0.-1.0]範圍內,如果超過了這個範圍,其計算結果將截取到與之最相近的值。 0表示完全透明,1表示完全不透明。 瀏覽器支持: (1).IE瀏覽器支持此屬性。 (2).火狐李藍旗支持此屬性。 (3).谷歌瀏覽器支持此屬性。 (4).opera瀏覽器支 ...
  • 一.什麼是CSS: CSS是Cascading Style Sheets的縮寫,通常為級聯樣式表。 CSS已經是網路不可或缺的元素,為瀏覽者呈現五彩繽紛的頁面效果起到了重要作用。 CSS是一種標記語言,不需要進行編譯,直接就可以在瀏覽器中執行。 二.使用CSS的優勢: (1).能夠極大提高代碼的簡潔 ...
  • 事件可以是瀏覽器行為也可以是用戶行為。 比如頁面文檔內容載入完成或者用戶滑鼠點擊等。 當一個事件發生的時候,需要特定的行為來響應這個事件,所以要為事件註冊事件處理函數。 代碼實例: 為按鈕註冊click事件處理函數,當點擊按鈕的時候,此函數就會執行。 本章節只是簡單介紹一下JavaScript事件的 ...
  • 一.HTML簡單介紹: HTML是Hypertext Markup Language的英文縮寫,即超文本標記語言。 它是一種標記語言而非編程語言,由瀏覽器解釋支持。 html文件是一種文本文件,可以用記事本打開,當然也可以用其他開發工具,比如dreamweaver和VS等等。通過在文本中添加各種標簽 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...