Skip to main content

 

applyMiddleware(...middleware)

Overview

Middleware is the suggested way to extend Redux with custom functionality. Middleware lets you wrap the store's dis 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.

Warning

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.

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 dis async actions in addition to normal actions.

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

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 (arguments): Functions that conform to the Redux middleware API. Each middleware receives Store's dis and getState functions as named arguments, and returns a function. That function will be given the next middleware's dis 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 dis method as the next parameter, thus ending the chain. So, the middleware signature is ({ getState, dis }) => next => action.

Returns

(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 dis', action)

// Call the next dis method in the middleware chain.
const returnValue = next(action)

console.log('state after dis', 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.dis({
type: 'ADD_TODO',
text: 'Understand the middleware'
})
// (These lines will be logged by the middleware:)
// will dis: { type: 'ADD_TODO', text: 'Understand the middleware' }
// state after dis: [ 'Use Redux', 'Understand the middleware' ]

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 dised 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 dis an action:
store.dis(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 `dis` so we can dis later.
// Thunk middleware knows how to turn thunk async actions into actions.
return function (dis) {
return fetchSecretSauce().then(
sauce => dis(makeASandwich(forPerson, sauce)),
error => dis(apologize('The Sandwich Shop', forPerson, error))
)
}
}

// Thunk middleware lets me dis thunk async actions
// as if they were actions!
store.dis(makeASandwichWithSecretSauce('Me'))

// It even takes care to return the thunk's return value
// from the dis, so I can chain Promises as long as I return them.
store.dis(makeASandwichWithSecretSauce('My wife')).then(() => {
console.log('Done!')
})

// In fact I can write action creators that dis
// actions and async actions from other action creators,
// and I can build my control flow with Promises.
function makeSandwichesForEverybody() {
return function (dis, 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 dis result.
return Promise.resolve()
}

// We can dis both plain object actions and other thunks,
// which lets us compose the asynchronous actions in a single flow.
return dis(makeASandwichWithSecretSauce('My Grandma'))
.then(() =>
Promise.all([
dis(makeASandwichWithSecretSauce('Me')),
dis(makeASandwichWithSecretSauce('My wife'))
])
)
.then(() => dis(makeASandwichWithSecretSauce('Our kids')))
.then(() =>
dis(
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
.dis(makeSandwichesForEverybody())
.then(() => response.send(renderToString(<MyApp store={store} />)))

// I can also dis 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 { dis, forPerson } = props

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

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

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

Tips

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

  • 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.

  • 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.

  • 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.

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