1. What is it#
Middleware is a type of software that sits between an application system and system software. It uses the basic services (functions) provided by system software to connect different parts or different applications of the application system on the network, achieving the purpose of resource sharing and function sharing.
In the previous article, we learned about the entire workflow of Redux. When an action is dispatched, the reducer immediately calculates the state. The whole process is a synchronous operation.
If you need to support asynchronous operations or support error handling and log monitoring, you can use middleware for this process.
In Redux, middleware is placed in the dispatch process to intercept and process actions. The diagram below shows this:
It is essentially a function that modifies the store.dispatch method. It adds other functions between dispatching actions and executing reducers.
2. Common Middleware#
There are many excellent Redux middleware, such as:
- redux-thunk: used for asynchronous operations
- redux-logger: used for logging
The above middleware needs to be registered through applyMiddlewares, which combines all middleware into an array and executes them in order.
Then pass it as the second parameter to createStore.
const store = createStore(
reducer,
applyMiddleware(thunk, logger)
);
redux-thunk#
redux-thunk is the recommended asynchronous processing middleware on the official website.
By default, dispatch(action) requires action to be a JavaScript object.
The redux-thunk middleware will check the data type you currently pass in. If it is a function, it will pass the parameter values (dispatch, getState) to the function.
- The dispatch function is used for us to dispatch actions again in the future.
- The getState function takes into account that some of our future operations need to rely on the original state, so that we can get some previous states.
So dispatch can be written in the following function form:
const getHomeMultidataAction = () => {
return (dispatch) => {
axios.get("http://xxx.xx.xx.xx/test").then(res => {
const data = res.data.data;
dispatch(changeBannersAction(data.banner.list));
dispatch(changeRecommendsAction(data.recommend.list));
})
}
}
redux-logger#
If you want to implement a logging feature, you can use the ready-made redux-logger.
import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
const logger = createLogger();
const store = createStore(
reducer,
applyMiddleware(logger)
);
This way, we can easily implement logging information through the middleware function.
3. Implementation Principle#
First, let's take a look at the source code of applyMiddlewares.
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
var store = createStore(reducer, preloadedState, enhancer);
var dispatch = store.dispatch;
var chain = [];
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
};
chain = middlewares.map(middleware => middleware(middlewareAPI));
dispatch = compose(...chain)(store.dispatch);
return {...store, dispatch}
}
}
All middleware is put into an array called chain, and then executed in a nested manner, and finally store.dispatch is executed. As you can see, the middleware internally (middlewareAPI) can access the getState and dispatch methods.
In the above learning, we learned the basic usage of redux-thunk.
Internally, dispatch is judged and the corresponding operation is executed. The principle is as follows:
function patchThunk(store) {
let next = store.dispatch;
function dispatchAndThunk(action) {
if (typeof action === "function") {
action(store.dispatch, store.getState);
} else {
next(action);
}
}
store.dispatch = dispatchAndThunk;
}
The principle of implementing a log output is also very simple, as follows:
let next = store.dispatch;
function dispatchAndLog(action) {
console.log("dispatching:", addAction(10));
next(addAction(5));
console.log("New state:", store.getState());
}
store.dispatch = dispatchAndLog;