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
自定義 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 行左右,是經過對它們的組合使用來達到最終的目的。