Analysis of Redux middleware applymiddleware

Time:2020-11-27

What is middleware

Middleware just wraps the dispatch method of the store. Technically, anything middleware can do can be implemented by manually wrapping the dispatch call, but unified management in the same place will make the expansion of the whole project much easier.

Repackaging the dispatch method

Why repackage dispatch

The role of middleware allows us to decide when to calldispatch, may be inpromiseAfter the function is executed or in theactionAfter the callback function is executed, this requires thedispatchThe function is repackaged to enable it to execute the method in the middleware function first, and then the realdispatchPass to middleware function:

let storeDispatch =  stroe.dispatch ; // retrieve the dispatch method of the store and save it

//Override the dispatch method
stroe.dispatch = function (action) {
  console.log('before dispatch')
  stroe.dispatch (action) // call the real dispatch method when appropriate
  console.log('after dispatch')
}

There is only one middleware

Logger Middleware

We can write oneloggerMiddleware to further understand:

function logger(store){
  return (dispatch)=>{
    return (action)=>{
        console.log("before logger");
        store.dispatch(action);
        console.log('after logger')
    }
  }
}

Middleware is a function composed of Coriolis. Each level is packaged with corresponding function parameters for us to call and executedispatchMethod is actually in the last returned function.

applyMiddleware

After all this talk, I haven’t seen the real oneapplyMiddlewareWhat does the function look like

Function applymiddleware (Middleware) {// @ params middleware array
  Return function (createstore) {// @ params create store function
    return function (reudcer) { // @params reducer
        Return store; // @ return returns the createstore (reducer)
    }
  }
}

applyMiddlewareIt is also a function of Coriolis combination, but the final return is astoreAccording to the above,reduxMiddleware deals withdispatchThe method is also used herestoreOfdispatchMethods repackage it

function applyMiddleware(middleware) {
  return function (createStore) {
    return function (reudcer) {
      let store = createStore(reudcer);
      Let dispatch = () = > {throw new error ("dispatch can't be used yet and has not been transformed into the next method")};
      dispatch = middleware(store)( store.dispatch ); // modify the packaged dispatch; corresponding to logger (store) (dispatch)
      return {
        ...store,
        dispatch
      }
    }
  }
}

//Pass in the corresponding value according to the function that applymeddleware needs to return
applyMiddleware(logger)(createStore)(reducer);

So every time I invoke in the componentstoreOfdispatchMethod is called through middlewareloggerPackageddispatch

function dispatch(action){
  console.log('before logger')
  store.dispatch (action) // this is the real call store.dispatch ;
  console.log('after logger')
}

Multiple Middleware

The situation of multiple middleware is complex. It is necessary to ensure that each middleware method can be executed and can be like the onion model,dispatchMethods can be executed in the innermost part:

function applyMiddleware(...middlewares) {
  return function (createStore) {
    return function (reudcer) {
      const store = createStore(reudcer)
      const middlewareAPI = {
        getState: store.getState,
        dispatch: (...args) => dispatch(...args),
      }
      
      //Traverse the middleware function first, execute the method inside and return to get a middleware array of the dispatch method
      const chain = middlewares.map((middleware) => middleware(middlewareAPI))
      
      //The compose function will combine the middleware array containing the dispatch method into a nested wrapper function
      const dispatch = compose(...chain)(store.dispatch)
      
      return {
        ...store,
        dispatch,
      }
    }
  }
}

herecomposeMethod to put thedispatchMethods are packaged together to enable middleware to execute layer by layer

//compose
function compose(...funcs) {
  if (funcs.length === 0) {
    return (args) => args
  }
  if (funcs.length === 1) {
    return funcs[0]
  }
  return funcs.reduce((a, next) => (...args) => a(next(...args)))
}

therereduceIt is not easy to understand. It may be easier to understand it by dividing it into functions

function compose(...middlewarw) {
  return function(storeDispatch) {

    function dispatch(index, action) {
      let fn = middlewarw[index]
      
      //Call back the next method in the callback to execute the next middleware function
      let next = () => dispatch(index + 1, action)
      //If there is another middleware, it will continue to execute, and the action will be passed in, otherwise it will be executed store.dispatch method
      fn ? fn(next)(action) : storeDispatch(action)
    }

    return (action) => dispatch(0, action)
  }
};

Common middleware

In addition, the source code implementation of the commonly used middleware is also described here

redux-thunk

function createThunkMiddleware(extraArgument) {
    return ({dispatch,getState}) => next => action => {
        if (typeof action == 'function') {
            return action(dispatch, getState, extraArgument);
        }
        return next(action);
    }
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;

redux-promise

function isPromise(obj) {
    return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
}
export default function promiseMiddleware({ dispatch }) {
    return next => action => {
        return isPromise(action.payload)
            ? action.payload
                .then(result => dispatch({ ...action, payload: result }))
                .catch(error => {
                    dispatch({ ...action, payload: error, error: true });
                    return Promise.reject(error);
                })
            : next(action);
    };
}

Recommended Today

Summary of recent use of gin

Recently, a new project is developed by using gin. Some problems are encountered in the process. To sum up, as a note, I hope it can help you. Cross domain problems Middleware: func Cors() gin.HandlerFunc { return func(c *gin.Context) { //Here you can use * or the domain name you specify c.Header(“Access-Control-Allow-Origin”, “*”) //Allow header […]