applyMiddleware()
applyMiddleware(...middleware)
中间件是推荐使用定制功能扩展 Redux 的方法。中间件可以让你打包存储中的dispatch
方法,获得乐趣和利润。中间件的关键特征是它是可组合的。多个中间件可以组合在一起,其中每个中间件不需要知道链中前后的内容。
中间件最常见的用例是支持异步操作,没有太多的样板代码或依赖于像 Rx 这样的库。除了正常操作外,它还可以让您分配异步操作。
例如,redux-thunk
允许动作创建者通过调度函数来反转控制。他们会收到dispatch
作为参数,并可能异步调用。这些功能被称为 thunk
。中间件的另一个例子是 redux-promise 。它允许您分派 Promise 异步操作,并在 Promise 解析时分派正常操作。
中间件并不是根植于createStore
中的也不是 Redux 体系结构的基本组成部分,但我们认为它足以在核心中获得支持。这样,在生态系统中有一种标准的扩展dispatch
的方式,不同的中间件可以在表现力和效用上竞争。
参数
- ...middleware(参数):符合Redux 中间件API 的函数。每个中间件接收Store的dispatch和getState功能命名的参数,并返回一个函数。该函数将被赋予next中间件的调度方法,并且预计会返回一个action调用函数,用可能有不同的参数调用next(action),或者在不同的时间,或者根本不调用它。链中的最后一个中间件将接收现实存储的dispatch方法作为next参数,从而结束链。所以,中间件签名是{ getState, dispatch }) => next => action。
返回
(功能)应用给定中间件的存储增强器。商店增强器签名是createStore => createStore',但是最简单的方式来应用它是作为最后一个enhancer参数传递它createStore()。
示例:自定义记录器中间件
import { createStore, applyMiddleware } from 'redux'
import todos from './reducers'
function logger{ getState }) {
return next => action => {
console.log('will dispatch', action)
// Call the next dispatch method in the middleware chain.
let returnValue = next(action)
console.log('state after dispatch', getState())
// This will likely be the action itself, unless
// a middleware further in chain changed it.
return returnValue
}
}
let store = createStore(
todos,
['Use Redux'],
applyMiddleware(logger)
)
store.dispatch{
type: 'ADD_TODO',
text: 'Understand the middleware'
})
// (These lines will be logged by the middleware:)
// will dispatch: { type: 'ADD_TODO', text: 'Understand the middleware' }
// state after dispatch: [ 'Use Redux', 'Understand the middleware' ]
示例:使用Thunk中间件进行异步操作
import { createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import * as reducers from './reducers'
let reducer = combineReducers(reducers)
// applyMiddleware supercharges createStore with middleware:
let store = createStore(reducer, applyMiddleware(thunk))
function fetchSecretSauce() {
return fetch('https://www.google.com/search?q=secret+sauce')
}
// These are the normal action creators you have seen so far.
// The actions they return can be dispatched without any middleware.
// However, they only express “facts” and not the “async flow”.
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
}
}
// Even without middleware, you can dispatch an action:
store.dispatch(withdrawMoney(100))
// But what do you do when you need to start an asynchronous action,
// such as an API call, or a router transition?
// Meet thunks.
// A thunk is a function that returns a function.
// This is a thunk.
function makeASandwichWithSecretSauce(forPerson) {
// Invert control!
// Return a function that accepts `dispatch` so we can dispatch later.
// Thunk middleware knows how to turn thunk async actions into actions.
return function (dispatch) {
return fetchSecretSauce().then(
sauce => dispatch(makeASandwich(forPerson, sauce)),
error => dispatch(apologize('The Sandwich Shop', forPerson, error))
)
}
}
// Thunk middleware lets me dispatch thunk async actions
// as if they were actions!
store.dispatch(makeASandwichWithSecretSauce('Me'))
// It even takes care to return the thunk's return value
// from the dispatch, so I can chain Promises as long as I return them.
store.dispatch(makeASandwichWithSecretSauce('My wife')).then(() => {
console.log('Done!')
})
// In fact I can write action creators that dispatch
// actions and async actions from other action creators,
// and I can build my control flow with Promises.
function makeSandwichesForEverybody() {
return function (dispatch, getState) {
if (!getState().sandwiches.isShopOpen) {
// You don't have to return Promises, but it's a handy convention
// so the caller can always call .then() on async dispatch result.
return Promise.resolve()
}
// We can dispatch both plain object actions and other thunks,
// which lets us compose the asynchronous actions in a single flow.
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')
)
)
}
}
// This is very useful for server side rendering, because I can wait
// until data is available, then synchronously render the app.
import { renderToString } from 'react-dom/server'
store
.dispatch(makeSandwichesForEverybody())
.then(() => response.send(renderToString(<MyApp store={store} />)))
// I can also dispatch a thunk async action from a component
// any time its props change to load the missing data.
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)
提示
- 中间件只包装商店的
dispatch
功能。从技术上讲,中间件可以做的任何事情都可以通过包装每个dispatch
调用来手动完成,但是在单一位置管理它并且在整个项目的规模上定义操作转换更容易。