Compose and applymeddleware in Redux

Time:2021-10-21

Compose method

Compose function definition

The compose method uses the of arraysreduceMethod to combine multiple methods into one method in a certain order, so as to achieve a functionAutomatic orderly executionThe effect of. The execution order of each method depends on how the reduce method is applied. The simple implementation of compose is as follows:

//Compose accepts multiple functions as input parameters fun1 fun2
function compose(...funs){
  const len = funs.length 
  //Handle two special cases, the number of functions is 0 | 1
  if(!len) return (...arg) => arg
  if(len === 1) return funs[0]
  //In the following combination, method B will be executed before method a
  return funs.reduce((a,b) => (...arg) => a(b(arg)))
  //In the following combination, method a will be executed before method B
  // return funs.reduce((a,b) => (...arg) => b(a(arg)))
}

The form of the combined function (a, b)

  1. The return value of the B function is ordinary data, which is used as the input parameter of the a function

    function b(){
      //If the return value is an ordinary object
      return {...}
    }
    function a(arg){
      //Arg data application processing
      return {...}
    }

    According to the above form, if a function wants to apply the return value of B function,It is best to know the return value of B function in advanceData types and field details, which will lead to differences between a and B methodsSerious coupling

  2. B returns a function as the input parameter of a function

    function b(){
      //The return value is a function
      return (...arg) => { 
     //...
      }
    }
    function a(next){
      //Next pre operation
      //Next () can operate next here
      //Next post operation
      return (...arg) => {
     //Next pre operation
     //Next () can operate next here
     //Next post operation
      }
    }

    Compared with the previous form, this form can make the relationship between functions reachDecouplingEffect, that is, function a does not need to care about the return value of function B, as long as it knows that its return value is a function and calls it at the appropriate time, which also controls the function call sequence, so as to achieveOnion like modelImplementation effect of.

    Applymiddleware method

    Applymiddleware function form

    Constructive closure, provide internally specified APIs for externally defined methods; The following example

    //Accept externally defined method array funs
    function applyMiddleware(...funs){
      const apis = {
     dispatch: () => ({})
      }
      //Traverse and execute externally defined methods to construct a closure; And return the returned value as a result
      return funs.map(fun => fun(apis))
    }

    The form of funs function to be manipulated

    To construct a closure, fun’sReturn valueIt should be a function that uses APIs.

    function myMiddleware1(apis){
      //Returns a method in which the methods exposed in Apis will be called to form a closure,
      return actions => {
      //APIs method call
      const { dispatch } = apis
     dispatch(actions)
      }
    }
    const myMiddlewareWrappers = applyMiddleware(myMiddleware1)
    //Mymiddlewarewappers is [actions = > {const {dispatch} = APIs; dispatch (actions);}]

    There may be a question: since it is only a function call in APIs, why should it be so complex and implemented with closures? Can the caller achieve his goal by directly calling the functions inside APIs?

  3. Application of closures:

    1. Closures can make usFunction external control function internalExecution logic, such as commonAnti shake and closurefunction
    2. The use of closures can realize the coritization of functions, so as to achieveFunction input parameter cacheAnd then achieve aSingle caseEffect of
  4. Why not call the method inside APIs directly:

    1. The internal methods of APIs do not exist from the beginning, but may be generated dynamically, which can be achieved through closures1. Single case effect mentioned in B
    2. Usually, we are not simply calling functions, but often with additional operations. Not applying closures will lead to the following effects
    function fun1(arg){
      console.log(arg)
      apis.dispatch(arg)
    }
    function fun2(arg){
      arg.b = 100
      apis.dispatch(arg)
    }
    //API method calls may be seen everywhere, but careful observation shows that API method calls are actually duplicate code
    Compare the effect of applying closures
    //There are no duplicate API internal function calls, and the constructed closure function will automatically execute the API internal methods
    //APIs was exposed, but not completely exposed
    const results = applyMiddleware(myMiddleware1,myMiddleware1)
    results[0](100)
    results[1](200)

    combination

    How to combine the two methods of compose and applymeddleware to get magic effects?
    First, consider the method in the form of mymiddleware defined above, and directly compose the method list obtained by applymiddleware

    const myMiddlewareWrappers = applyMiddleware(myMiddleware1, myMiddleware2)
    //Combine the method list into one method through compose
    const result = compose(...myMiddlewareWrappers)
    //What is the form of result at this time?
    //According to the function of compose, it is not difficult to imagine that result is a function
    // actions => myMiddleware1(myMiddleware2(actions))

    At this point, composite has been combined with applymeddleware.
    However, if you carefully observe the form of mymiddleware1 and 2 functions, you will find some problems. The return value of mymiddleware1 and 2 methods is not a function. Through the analysis of compose at the beginning, you can get that this form will bring about differences between functionsSerious couplingProblems. Therefore, one-step transformation of mymiddleware is required, as follows:

    function myMiddleware3(apis){
      //Returns a method in which the methods exposed in Apis will be called to form a closure,
      //At the same time, in order to ensure that the input parameter of each function in the compose process is still a function, you need to re-enter the following return values
      //One layer packaging
      return next => actions => {
     // ...
     next(actions)
     // ...
      }
    }

    magic

    However, we are still not satisfied with the above results. The call of APIs internal function is not in the hands of the function caller, that is, the function caller can only pass in one actions parameter, but cannot control the specific execution time of APIs function. How to achieveControl reversalThe effect of?
    Actions can actually be a method. At this time, you can pass APIs to actions by passing parameters, so as to give the control of APIs to the real function caller. as follows

    function myMiddleware2(apis){
     //Returns a method in which the methods exposed in Apis will be called to form a closure,
     return next => actions => {
       //Control reversal
       actions(apis)
     }
      }
    
      //It can be compatible with mymiddleware2 and mymiddleware1 functions
      //That is, when the caller needs to control APIs, it can pass in actions in the form of function, otherwise it can pass in parameters in the form specified in APIs
      function myMiddleware2(apis){
     //Returns a method in which the methods exposed in Apis will be called to form a closure,
     return next => actions => {
       //Control reversal
       if(typeof actions === 'function') return actions(apis);
       next(actions)
     }
    }