Redux框架之applyMiddleware()講解

applyMiddleware()

  • applyMiddleware(...middlewares)react

  • 使用包含自定義功能的 middleware 來擴展 Redux 是一種推薦的方式。Middleware 可讓你包裝 store 的 dispatch 方法來達到你想要的目的。同時, middleware 還擁有「可組合」這一關鍵特性。多個 middleware 能夠被組合到一塊兒使用,造成 middleware 鏈。其中,每一個 middleware 都不須要關心鏈中它先後的 middleware 的任何信息。git

  • Middleware 最多見的使用場景是無需引用大量代碼或依賴相似 Rx 的第三方庫實現異步 actions。這種方式可讓你像 dispatch 通常的 actions 那樣 dispatch 異步 actions。github

  • 例如,redux-thunk 支持 dispatch function,以此讓 action creator 控制反轉。被 dispatch 的 function 會接收 dispatch 做爲參數,而且能夠異步調用它。這類的 function 就稱爲 thunk。另外一個 middleware 的示例是 redux-promise。它支持 dispatch 一個異步的 Promise action,而且在 Promise resolve 後能夠 dispatch 一個普通的 action。redux

  • Middleware 並不須要和 createStore 綁在一塊兒使用,也不是 Redux 架構的基礎組成部分,但它帶來的益處讓咱們認爲有必要在 Redux 核心中包含對它的支持。所以,雖然不一樣的 middleware 可能在易用性和用法上有所不一樣,它仍被做爲擴展 dispatch 的惟一標準的方式。promise

參數

  • ...middlewares (arguments): 遵循 Redux middleware API 的函數。每一個 middleware 接受 Store 的 dispatch 和 getState 函數做爲命名參數,並返回一個函數。該函數會被傳入 被稱爲 next 的下一個 middleware 的 dispatch 方法,並返回一個接收 action 的新函數,這個函數能夠直接調用 next(action),或者在其餘須要的時刻調用,甚至根本不去調用它。調用鏈中最後一個 middleware 會接受真實的 store 的 dispatch 方法做爲 next 參數,並藉此結束調用鏈。因此,middleware 的函數簽名是 ({ getState, dispatch }) => next => action。

返回值

  • (Function) 一個應用了 middleware 後的 store enhancer。這個 store enhancer 就是一個函數,而且須要應用到 createStore。它會返回一個應用了 middleware 的新的 createStore。

示例

自定義 Logger Middlewarebash

import { createStore, applyMiddleware } from 'redux'
import todos from './reducers'

function logger({ getState }) {
  return (next) => (action) => {
    console.log('will dispatch', action)

    // 調用 middleware 鏈中下一個 middleware 的 dispatch。
    let returnValue = next(action)

    console.log('state after dispatch', getState())

    // 通常會是 action 自己,除非
    // 後面的 middleware 修改了它。
    return returnValue
  }
}

let createStoreWithMiddleware = applyMiddleware(logger)(createStore)
let store = createStoreWithMiddleware(todos, [ 'Use Redux' ])

store.dispatch({
  type: 'ADD_TODO',
  text: 'Understand the middleware'
})
// (將打印以下信息:)
// will dispatch: { type: 'ADD_TODO', text: 'Understand the middleware' }
// state after dispatch: [ 'Use Redux', 'Understand the middleware' ]
複製代碼

使用 Thunk Middleware 來作異步 Action架構

import { createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import * as reducers from './reducers'

// 調用 applyMiddleware,使用 middleware 加強 createStore:
let createStoreWithMiddleware = applyMiddleware(thunk)(createStore)

// 像原生 createStore 同樣使用。
let reducer = combineReducers(reducers)
let store = createStoreWithMiddleware(reducer)

function fetchSecretSauce() {
  return fetch('https://www.google.com/search?q=secret+sauce')
}

// 這些是你已熟悉的普通 action creator。
// 它們返回的 action 不須要任何 middleware 就能被 dispatch。
// 可是,他們只表達「事實」,並不表達「異步數據流」

function makeASandwich(forPerson, secretSauce) {
  return {
    type: 'MAKE_SANDWICH',
    forPerson,
    secretSauce
  }
}

function apologize(fromPerson, toPerson, error) {
  return {
    type: 'APOLOGIZE',
    fromPerson,
    toPerson,
    error
  }
}

function withdrawMoney(amount) {
  return {
    type: 'WITHDRAW',
    amount
  }
}

// 即便不使用 middleware,你也能夠 dispatch action:
store.dispatch(withdrawMoney(100))

// 可是怎樣處理異步 action 呢,
// 好比 API 調用,或者是路由跳轉?

// 來看一下 thunk。
// Thunk 就是一個返回函數的函數。
// 下面就是一個 thunk。

function makeASandwichWithSecretSauce(forPerson) {

  // 控制反轉!
  // 返回一個接收 `dispatch` 的函數。
  // Thunk middleware 知道如何把異步的 thunk action 轉爲普通 action。

  return function (dispatch) {
    return fetchSecretSauce().then(
      sauce => dispatch(makeASandwich(forPerson, sauce)),
      error => dispatch(apologize('The Sandwich Shop', forPerson, error))
    )
  }
}

// Thunk middleware 可讓咱們像 dispatch 普通 action
// 同樣 dispatch 異步的 thunk action。

store.dispatch(
  makeASandwichWithSecretSauce('Me')
)

// 它甚至負責回傳 thunk 被 dispatch 後返回的值,
// 因此能夠繼續串連 Promise,調用它的 .then() 方法。

store.dispatch(
  makeASandwichWithSecretSauce('My wife')
).then(() => {
  console.log('Done!')
})

// 實際上,能夠寫一個 dispatch 其它 action creator 裏
// 普通 action 和異步 action 的 action creator,
// 並且可使用 Promise 來控制數據流。

function makeSandwichesForEverybody() {
  return function (dispatch, getState) {
    if (!getState().sandwiches.isShopOpen) {

      // 返回 Promise 並非必須的,但這是一個很好的約定,
      // 爲了讓調用者可以在異步的 dispatch 結果上直接調用 .then() 方法。

      return Promise.resolve()
    }

    // 能夠 dispatch 普通 action 對象和其它 thunk,
    // 這樣咱們就能夠在一個數據流中組合多個異步 action。

    return dispatch(
      makeASandwichWithSecretSauce('My Grandma')
    ).then(() =>
      Promise.all([
        dispatch(makeASandwichWithSecretSauce('Me')),
        dispatch(makeASandwichWithSecretSauce('My wife'))
      ])
    ).then(() =>
      dispatch(makeASandwichWithSecretSauce('Our kids'))
    ).then(() =>
      dispatch(getState().myMoney > 42 ?
        withdrawMoney(42) :
        apologize('Me', 'The Sandwich Shop')
      )
    )
  }
}

// 這在服務端渲染時頗有用,由於我能夠等到數據
// 準備好後,同步的渲染應用。

import { renderToString } from 'react-dom/server'

store.dispatch(
  makeSandwichesForEverybody()
).then(() =>
  response.send(renderToString(<MyApp store={store} />))
)

// 也能夠在任何致使組件的 props 變化的時刻
// dispatch 一個異步 thunk action。

import { connect } from 'react-redux'
import { Component } from 'react'

class SandwichShop extends Component {
  componentDidMount() {
    this.props.dispatch(
      makeASandwichWithSecretSauce(this.props.forPerson)
    )
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.forPerson !== this.props.forPerson) {
      this.props.dispatch(
        makeASandwichWithSecretSauce(nextProps.forPerson)
      )
    }
  }

  render() {
    return <p>{this.props.sandwiches.join('mustard')}</p>
  }
}

export default connect(
  state => ({
    sandwiches: state.sandwiches
  })
)(SandwichShop)
複製代碼

總結

  • Middleware 只是包裝了 store 的 dispatch 方法。技術上講,任何 middleware 能作的事情,均可能經過手動包裝 dispatch 調用來實現,可是放在同一個地方統一管理會使整個項目的擴展變的容易得多。app

  • 若是除了 applyMiddleware,你還用了其它 store enhancer,必定要把 applyMiddleware 放到組合鏈的前面,由於 middleware 可能會包含異步操做。好比,它應該在 redux-devtools 前面,不然 DevTools 就看不到 Promise middleware 裏 dispatch 的 action 了。框架

  • 若是你想有條件地使用 middleware,記住只 import 須要的部分:dom

let middleware = [ a, b ]
if (process.env.NODE_ENV !== 'production') {
  let c = require('some-debug-middleware')
  let d = require('another-debug-middleware')
  middleware = [ ...middleware, c, d ]
}
const createStoreWithMiddleware = applyMiddleware(...middleware)(createStore)
複製代碼

這樣作有利於打包時去掉不須要的模塊,減少打包文件大小。

  • 有想過 applyMiddleware 本質是什麼嗎?它確定是比 middleware 還強大的擴展機制。實際上,applyMiddleware 只是被稱爲 Redux 最強大的擴展機制的 store enhancers 中的一個範例而已。你不太可能須要實現本身的 store enhancer。另外一個 store enhancer 示例是 redux-devtools。Middleware 並無 store enhancer 強大,但開發起來倒是更容易的。

  • Middleware 聽起來比實際難一些。真正理解 middleware 的惟一辦法是瞭解現有的 middleware 是如何工做的,並嘗試本身實現。須要的功能可能錯綜複雜,可是你會發現大部分 middleware 實際上很小,只有 10 行左右,是經過對它們的組合使用來達到最終的目的。

更多文章

  • 做者React Native開源項目OneM【500+ star】地址(按照企業開發標準搭建框架完成開發的):github.com/guangqiang-…:歡迎小夥伴們 star
  • 做者簡書主頁:包含60多篇RN開發相關的技術文章www.jianshu.com/u/023338566… 歡迎小夥伴們:多多關注多多點贊
  • 做者React Native QQ技術交流羣:620792950 歡迎小夥伴進羣交流學習
  • 友情提示:在開發中有遇到RN相關的技術問題,歡迎小夥伴加入交流羣(620792950),在羣裏提問、互相交流學習。交流羣也按期更新最新的RN學習資料給你們,謝謝你們支持!

歡迎小夥伴們掃描下方二維碼加入RN技術交流QQ羣(500人+)

QQ羣二維碼,500+ RN工程師在等你加入哦
相關文章
相關標籤/搜索