Principle of Redux Middleware

Time:2021-2-25

Principle of Redux Middleware

Principle of Redux Middleware

Middleware execution sequence

The following middleware is applied: [a, B, C],

The whole process of executing action is a → B → C → dispatch → C → B → a

==AfterapplyMiddlewareAfter the methoddispatchMethod is similar to the following, called somewhere store.dispatch Then the action is passed in and executed according to the onion model==

(action) => {
    //...
    next(action)
    //...
}

==Far rightnext(action)Returned is the data passed in by the applicationaction==This behavior is determined by the return value of the dispatch method in the Redux createstore source code, but generally it isreturn next(action),

In front of the middlewarenext(action)The value of is the return value of the latter middleware

Small example


const middleware1 = ({getState, dispatch}) => next => action => {

    console.log(`middleware1 before next action `)
    console.log(next)
    next(action)
    console.log(`middleware1 after next action `)
}
const middleware2 = ({getState, dispatch}) => next => action => {

    console.log(`middleware2 before next action `)
    console.log(next)
    next(action)
    console.log(`middleware2 after next action `)
}
const middleware3 = ({getState, dispatch}) => next => action => {

    console.log(`middleware3 before next action `)
    console.log(next)
    next(action)
    console.log(`middleware3 after next action `)
}



const sagaMiddleware = createSagaMiddleware()
const store = createStore(reducer, applyMiddleware(middleware1, middleware2, middleware3))

Principle of Redux Middleware

Dispatch method wrapped by middleware

It’s similar tofunc1(func2(store.dispatch))Functions of this form

==The next method is actually the method of the middleware on the right side of the middleware. You need to pass action to it. ==

(action) => {
    //Next in func2 is store.dispatch
    next(action);
}

==The actual calling order is the reverse of that of the incoming middleware==

let store = applyMiddleware(Middleware1,Middleware2,Middleware3)(createStore)(rootReducer);

The actual execution is in sequence store.dispatch ->Middleware3->Middleware2->Middleware1。

The dispatch in middlewar API is wrapped in a common function

var middlewareAPI = {

getState: store.getState,
dispatch: (action) => dispatch(action)

};

It’s not used directly dispatch:dispatch Instead, it uses dispatch: (action) = > dispatch (action) to = = if dispatch:dispatch , then the same dispatch (closure) is actually referenced in all middleware. If a middleware modifies the dispatch, it will lead to a series of problems = =, but if you use thedispatch: (action) => dispatch(action)This problem can be avoided.

Compose source code

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  const last = funcs[funcs.length - 1]
  const rest = funcs.slice(0, -1)
  return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}



Applymiddleware source code

Here chain is an array of functions. Then execute compose (… Chain)( store.dispatch )。
The last function of the chain array receives store.dispatch Function as a parameter, or return a function, receive action as a parameter. The returned function is the parameter of the penultimate function in chain, that is, the parameter next

[
    next => action => {
        
        return next(action)
    }
]

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
    }
  }
}

Redux thunk source code

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;

reference resources

https://github.com/MrErHu/blo…

https://zhuanlan.zhihu.com/p/…

https://zhuanlan.zhihu.com/p/…