Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
applyMiddleware(...middleware)
Resumen
El middleware es la forma recomendada de extender Redux con funcionalidades personalizadas. Te permite envolver el método dispatch de la tienda para obtener mayor flexibilidad. La característica clave del middleware es que es componible. Puedes combinar múltiples middlewares, donde cada uno no necesita conocer qué viene antes o después en la cadena.
No deberías necesitar llamar a applyMiddleware directamente. El método configureStore de Redux Toolkit añade automáticamente un conjunto predeterminado de middleware a la tienda, o puede aceptar una lista de middleware para agregar.
El caso de uso más común para middleware es dar soporte a acciones asíncronas sin mucho código repetitivo o dependencias en librerías como Rx. Lo logra permitiéndote despachar acciones asíncronas además de acciones normales.
Por ejemplo, redux-thunk permite que los creadores de acciones inviertan el control despachando funciones. Estas reciben dispatch como argumento y pueden llamarlo de forma asíncrona. Tales funciones se llaman thunks. Otro ejemplo es redux-promise, que te permite despachar una acción asíncrona Promise y despacha una acción normal cuando la Promise se resuelve.
El método original createStore de Redux no entiende los middleware de forma nativa: debe configurarse con applyMiddleware para añadir ese comportamiento. Sin embargo, el método configureStore de Redux Toolkit añade soporte para middleware por defecto.
Argumentos
...middleware(argumentos): Funciones que cumplen con la API de middleware de Redux. Cada middleware recibe las funcionesdispatchygetStatedeStorecomo argumentos nombrados, y devuelve una función. A esta función se le pasará el método dispatch del middlewarenext, y se espera que devuelva una función deactionque llame anext(action)con un argumento potencialmente diferente, en otro momento, o quizás sin llamarlo. El último middleware de la cadena recibirá el métododispatchreal de la tienda como parámetronext, finalizando la cadena. Así, la firma del middleware es({ getState, dispatch }) => next => action.
Devuelve
(Función) Un potenciador de tienda que aplica el middleware dado. La firma del potenciador es createStore => createStore, pero la forma más sencilla de aplicarlo es pasarlo a createStore() como último argumento enhancer.
Ejemplos
Ejemplo: Middleware de registro personalizado
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' ]
Ejemplo: Usar middleware thunk para acciones asíncronas
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)
Consejos
-
El middleware solo envuelve la función
dispatchde la tienda. Técnicamente, cualquier cosa que haga un middleware podrías hacerla manualmente envolviendo cada llamada adispatch, pero es más fácil gestionarlo en un solo lugar y definir transformaciones de acciones a escala de todo el proyecto. -
Si usas otros potenciadores de tienda además de
applyMiddleware, asegúrate de colocarapplyMiddlewareantes que ellos en la cadena de composición, porque el middleware es potencialmente asíncrono. Por ejemplo, debe ir antes de redux-devtools, ya que de lo contrario las DevTools no verán las acciones crudas emitidas por el middleware de Promise y similares. -
Si necesitas aplicar un middleware condicionalmente, asegúrate de importarlo solo cuando sea necesario:
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)
)Esto facilita que las herramientas de empaquetado eliminen módulos innecesarios y reduce el tamaño de tus builds.
-
¿Alguna vez te preguntaste qué es
applyMiddlewarerealmente? Debería ser un mecanismo de extensión más potente que el propio middleware. De hecho,applyMiddlewarees un ejemplo del mecanismo de extensión más poderoso de Redux llamado store enhancer. Es muy poco probable que necesites escribir un store enhancer tú mismo. Otro ejemplo es redux-devtools. El middleware es menos potente que un store enhancer, pero es más sencillo de escribir. -
El middleware suena mucho más complejo de lo que realmente es. La única forma de entenderlo realmente es ver cómo funcionan los middlewares existentes e intentar escribir el tuyo. La anidación de funciones puede parecer intimidante, pero la mayoría de los middlewares que encontrarás son, de hecho, de unas 10 líneas, y esta anidación y composabilidad es lo que hace poderoso al sistema de middleware.
-
Para aplicar múltiples store enhancers, puedes usar
compose().