In Redux, middleware specifically refers to an enhancer
passed to the store via createStore
. When a dispatch
is made, the middleware intercepts the action
before it reaches the reducer
. The middleware can then:
You’ll use Redux middleware for logging information about the store and making asynchronous API requests, but you can also use it for crash reporting, routing, and many other applications.
When you finish this article, you should be able to use the React-Redux library’s applyMiddleware
function to configure one or more middleware when creating a store.
Recall the redux
library’s createStore
function used to instantiate a store. createStore
accepts three arguments (reducer, preloadedState, enhancer
); middleware is given to the store via the optional enhancer
argument.
Consider the following example, where you import a third-party logger
middleware:
// ./src/store.js
import { createStore, applyMiddleware } from 'redux';
import logger from 'redux-logger';
import rootReducer from './reducers/rootReducer';
const configureStore = (preloadedState = {}) => {
return createStore(
rootReducer,
preloadedState,
applyMiddleware(logger),
);
};
export default configureStore;
Any actions dispatched to the store
pass through the logger
middleware, which prints the store’s state before and after the action
is processed.
Note:
applyMiddleware()
accepts multiple arguments, so you can also apply more middleware if necessary.
In addition to importing third-party middlewares such as the above logger
, you’ll sometimes need to roll your own. All middleware functions need to conform to the same signature in order to be compatible with the store and other middlewares.
A function signature is the set of inputs and output of a function. A Redux middleware must always have the following signature:
Every middleware receives the store
as an argument and returns a function that takes the next
link in the middleware chain as an argument. That function returns another function that receives the action
and then triggers any side effects before returning the result of next(action)
. Side effects can include triggering AJAX requests, logging to the console, and more. Side effects can also happen after next(action)
is called, like so:
const middleware = store => next => action => {
const result = next(action);
// side effect using `result`
return result;
};
logger
middlewareYou can hand-roll the logger
middleware you imported above. It should print out the state before and after each dispatch, allowing you to check if your reducers are working as expected. This middleware should:
next
middleware,action
.The body of the innermost function is where you want to do your logging. That function should:
console.log
the action
console.log
the result of store.getState()
(pre-dispatch)next(action)
to pass the action on to the rest of the middlewares, and eventually, the reducerresult
of the next(action)
variable, to be returned laterconsole.log
the new store.getState()
result
const logger = store => next => action => {
console.log('Action received:', action);
console.log('State pre-dispatch:', store.getState());
let result = next(action);
console.log('State post-dispatch:', store.getState());
return result;
};
Now, whenever you dispatch an action, you’ll see its effect on the store.
redux-logger
middlewareAs you move forward with Redux, you’ll want to have access to your store’s state for debugging purposes. Including the redux-logger
npm package and adding it as a middleware gives you access (through the console) to the previous state, action, and next state with each dispatch. This is incredibly convenient for debugging purposes and avoids such unpleasantness as attaching the store
to the window
.
Follow the example below to include it in your projects:
redux-logger
package:redux-logger
to applyMiddleware
when creating your store:Note: logger
must be the last middleware passed into applyMiddleware
, otherwise it will log the thunk and any involved promises. You’ll learn about thunks and react-thunk
in the next article.
// ./src/store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import rootReducer from './reducers/rootReducer';
const configureStore = (preloadedState = {}) => {
return createStore(
rootReducer,
preloadedState,
applyMiddleware(thunk, logger),
);
};
export default configureStore;
In this article, you learned how to use the React-Redux library’s applyMiddleware
function to configure one or more middleware when creating a store.