Skip to main content

applyMiddleware(...middleware)

概述

¥Overview

中间件是使用自定义功能扩展 Redux 的建议方法。中间件可让你封装存储的 dispatch 方法以获得乐趣和利润。中间件的关键特性是它是可组合的。多个中间件可以组合在一起,其中每个中间件不需要知道链中它之前或之后的内容。

¥Middleware is the suggested way to extend Redux with custom functionality. Middleware lets you wrap the store's dispatch method for fun and profit. The key feature of middleware is that it is composable. Multiple middleware can be combined together, where each middleware requires no knowledge of what comes before or after it in the chain.

警告

你不必直接致电 applyMiddleware。Redux Toolkit 的 configureStore 方法 自动向存储添加一组默认的中间件,或者可以接受要添加的中间件列表。

¥You shouldn't have to call applyMiddleware directly. Redux Toolkit's configureStore method automatically adds a default set of middleware to the store, or can accept a list of middleware to add.

中间件最常见的用例是支持异步操作,无需太多样板代码或依赖于 Rx 等库。除了正常操作之外,它还允许你调度 异步操作 来实现这一点。

¥The most common use case for middleware is to support asynchronous actions without much boilerplate code or a dependency on a library like Rx. It does so by letting you dispatch async actions in addition to normal actions.

例如,redux-thunk 让动作创建者通过分派函数来反转控制。他们将接收 dispatch 作为参数,并可能异步调用它。此类函数称为 thunk。中间件的另一个例子是 redux-promise。它允许你分派 Promise 异步操作,并在 Promise 解析时分派正常操作。

¥For example, redux-thunk lets the action creators invert control by dispatching functions. They would receive dispatch as an argument and may call it asynchronously. Such functions are called thunks. Another example of middleware is redux-promise. It lets you dispatch a Promise async action, and dispatches a normal action when the Promise resolves.

原来的 Redux createStore 方法不明白开箱即用的中间件是什么 - 必须使用 applyMiddleware 配置才能添加该行为。不过,Redux Toolkit 的 configureStore 方法 默认自动添加中间件支持。

¥The original Redux createStore method does not understand what middleware are out of the box - it has to be configured with applyMiddleware to add that behavior. However, Redux Toolkit's configureStore method automatically adds middleware support by default.

参数

¥Arguments

  • ...middleware(参数):符合 Redux 中间件 API 的函数。每个中间件接收 StoredispatchgetState 函数作为命名参数,并返回一个函数。该函数将被赋予 next 中间件的调度方法,并且预计返回 action 的函数,该函数使用可能不同的参数或在不同的时间调用 next(action),或者可能根本不调用它。链中的最后一个中间件将接收真实存储的 dispatch 方法作为 next 参数,从而结束链。所以,中间件签名是 ({ getState, dispatch }) => next => action

    ¥...middleware (arguments): Functions that conform to the Redux middleware API. Each middleware receives Store's dispatch and getState functions as named arguments, and returns a function. That function will be given the next middleware's dispatch method, and is expected to return a function of action calling next(action) with a potentially different argument, or at a different time, or maybe not calling it at all. The last middleware in the chain will receive the real store's dispatch method as the next parameter, thus ending the chain. So, the middleware signature is ({ getState, dispatch }) => next => action.

返回

¥Returns

(功能)应用给定中间件的存储增强器。存储增强器签名是 createStore => createStore,但应用它的最简单方法是将其作为最后一个 enhancer 参数传递给 createStore()

¥(Function) A store enhancer that applies the given middleware. The store enhancer signature is createStore => createStore but the easiest way to apply it is to pass it to createStore() as the last enhancer argument.

示例

¥Examples

示例:自定义日志器中间件

¥Example: Custom Logger Middleware

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.
const 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
}
}

const 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 中间件进行异步操作

¥Example: Using Thunk Middleware for Async Actions

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

const reducer = combineReducers(reducers)
// applyMiddleware supercharges createStore with middleware:
const 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 React from 'react'
import { connect } from 'react-redux'

function SandwichShop(props) {
const { dispatch, forPerson } = props

useEffect(() => {
dispatch(makeASandwichWithSecretSauce(forPerson))
}, [forPerson])

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

export default connect(state => ({
sandwiches: state.sandwiches
}))(SandwichShop)

提示

¥Tips

  • 中间件仅封装存储的 dispatch 功能。从技术上讲,中间件可以做的任何事情,你都可以通过封装每个 dispatch 调用来手动完成,但在单个位置进行管理并在整个项目的规模上定义操作转换会更容易。

    ¥Middleware only wraps the store's dispatch function. Technically, anything a middleware can do, you can do manually by wrapping every dispatch call, but it's easier to manage this in a single place and define action transformations on the scale of the whole project.

  • 如果除了 applyMiddleware 之外还使用其他存储增强器,请确保在组合链中将 applyMiddleware 放在它们之前,因为中间件可能是异步的。例如,它应该在 redux-devtools 之前,否则 DevTools 将看不到 Promise 中间件等发出的原始操作。

    ¥If you use other store enhancers in addition to applyMiddleware, make sure to put applyMiddleware before them in the composition chain because the middleware is potentially asynchronous. For example, it should go before redux-devtools because otherwise the DevTools won't see the raw actions emitted by the Promise middleware and such.

  • 如果你想有条件地应用中间件,请确保仅在需要时导入它:

    ¥If you want to conditionally apply a middleware, make sure to only import it when it's needed:

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

    const store = createStore(
    reducer,
    preloadedState,
    applyMiddleware(...middleware)
    )

    这使得打包工具更容易删除不需要的模块并减少构建的大小。

    ¥This makes it easier for bundling tools to cut out unneeded modules and reduces the size of your builds.

  • 有没有想过 applyMiddleware 本身是什么?它应该是一个比中间件本身更强大的扩展机制。事实上,applyMiddleware 是最强大的 Redux 扩展机制 存储增强剂 的一个例子。你不太可能想要自己编写存储增强器。存储增强器的另一个例子是 redux-devtools。中间件的功能不如存储增强器强大,但更容易编写。

    ¥Ever wondered what applyMiddleware itself is? It ought to be an extension mechanism more powerful than the middleware itself. Indeed, applyMiddleware is an example of the most powerful Redux extension mechanism called store enhancers. It is highly unlikely you'll ever want to write a store enhancer yourself. Another example of a store enhancer is redux-devtools. Middleware is less powerful than a store enhancer, but it is easier to write.

  • 中间件听起来比实际复杂得多。真正理解中间件的唯一方法是了解现有中间件如何工作,并尝试编写自己的中间件。函数嵌套可能令人生畏,但事实上,你会发现大多数中间件都是 10 行的,嵌套和可组合性使中间件系统变得强大。

    ¥Middleware sounds much more complicated than it really is. The only way to really understand middleware is to see how the existing middleware works, and try to write your own. The function nesting can be intimidating, but most of the middleware you'll find are, in fact, 10-liners, and the nesting and composability is what makes the middleware system powerful.

  • 要应用多个存储增强器,你可以使用 compose()

    ¥To apply multiple store enhancers, you may use compose().