Why do we need to create custom middleware as curried function in Redux

痴心易碎 提交于 2019-12-24 07:39:46

问题


I am new to Redux and was learning how to create middleware for React app. The question I would like to ask on the creation of custom middleware as CURRIED function, that is, why currying? what is the benefit of currying in the context of the creation of middleware. That is, instead of writing const middleware = store=>next=>action=>{} why not write const middleware = (store,next,action)=>{}.


回答1:


Why Currying

Actually Currying tries to minimize the number of changes to a program’s state known as side effects by using immutable data and pure (no side effects) functions. Currying is helpful, when you want to write little code modules that can be reused and configured with ease, to avoid frequently calling a function with the same argument and in partial applications.

Benefit of currying in the context of middleware

By using currying, redux basically provides you three hook functions:

  • const firstFunction = logger(store)
  • const secondFunction = firstFunction(next)
  • const thirdFunction = secondFunction(action)

Now every one of the above function has its own utilization depending upon the different scenarios. Store has been created in the first function, while second function gives you the next middleware and in the third function we can do something with the previous middleware's result action.

Co-Author of Redux, Dan Abramov also answered about this issue. You can check his thoughts on this here




回答2:


It's because of how compose works.

Say I have 2 functions: plus2 and times3 then I can compose them with:

const plus2Times3 = compose(plus2,times3);
plus2Times3(1);//(1+2)*3=9
plus2Times3(2);//(2+2)*3=12

What compose did with plus2 and times3 is: (...arg)=>times3(...plus2(...arg))

Difficult enough to understand with simple functions but how about curried functions?

All middle ware functions receive the same state so if I have an array of middleware functions I can do:

//middewares is [store=>next=>action=>next(action),store=>next=>action=>next(action)]
const mw = middlewares.map(fn=>fn({getState,dispatch}));
//mw is [next=>action=>next(action),next=>action=>next(action)]

Now I have an array of functions that are next=>action=>next(action) where next is a function that takes an action.

So if I provide next=>action=>next(action) with a next function I get a function that is a next function: action=>does something. This means redux can use compose to create one function out of an array of functions:

const middeware = compose(middlewares.map(fn=>fn({getState,dispatch}));
//middeware is now next=>action=>do something
//redux can execute it with: middleware(reducer)(action)

Here is an example of a self written redux combined with react-redux that shows how compose is used to create one function out of an array of middleware functions.

Here is some code to demonstrate how to easily compose middleware functions:

//implementation of compose is very simple, I added taking
//  out anything that isn't a function
const compose = (...functions) => {
  return (
    functions
      //take out non functions
      .filter(e => typeof e === 'function')
      .reduce((result, fn) => {
        return (...args) => result(fn(...args));
      })
  );
};
const middeWares = [
  store => next => action => {
    console.log('1', action);
    next({ ...action, counter: action.counter + 1 });
  },
  store => next => action => {
    console.log('2', action);
    next({ ...action, counter: action.counter + 1 });
  },
  store => next => action => {
    console.log('3', action);
    next({ ...action, counter: action.counter + 1 });
  },
];
//just pass in 88 as store, we don't use store
const middleWare = compose(...middeWares.map(x => x(88)));

//executing the middleware
//pass in id function as next function (probably reducer in redux)
middleWare(x=>x)({counter:86});

Redux could have chosen for any signature for middle ware as long as it has store, next and action. The following example is mostly the same as the previous one but maps middleware functions to something that can be composed:

const compose = (...functions) => {
  return (
    functions
      //take out non functions
      .filter(e => typeof e === 'function')
      .reduce((result, fn) => {
        return (...args) => result(fn(...args));
      })
  );
};
//middleware could also have (action,next,store) signature
const middeWares = [
  (action,next,store) => {
    console.log('1', action);
    next({ ...action, counter: action.counter + 1 });
  },
  (action,next,store) => {
    console.log('2', action);
    next({ ...action, counter: action.counter + 1 });
  },
  (action,next,store) => {
    console.log('3', action);
    next({ ...action, counter: action.counter + 1 });
  },
];
//using 88 for store, we can map the middleware to s=>n=>a=>...
const middleWare = compose(...middeWares.map(x => n=>a=>x(a,n,88)));
//pass in id function as next function (could be dispatch or reducer)
//  depending on how redux is written
middleWare(x=>x)({counter:86});



回答3:


Ok, I will try to clarify your confusion. First we understand the purpose of middleware

Middleware is some code you can put between the framework receiving a request, and the framework generating a response

So as currying allows you to return a function from another function, you can see it this way that before fetching something to the framework we would like to do some additional operations, so if as you are suggesting we do it like

const middleware = (store,next,action)=>{}

We are directly calling the framework code, currying allows us to interrupt and do our stuff

Hope it helps



来源:https://stackoverflow.com/questions/58284702/why-do-we-need-to-create-custom-middleware-as-curried-function-in-redux

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!