Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

middleware - Why currying rather than (store, action, next) -> #534

Closed
phzbox opened this issue Aug 15, 2015 · 7 comments
Closed

middleware - Why currying rather than (store, action, next) -> #534

phzbox opened this issue Aug 15, 2015 · 7 comments

Comments

@phzbox
Copy link

phzbox commented Aug 15, 2015

Hi, the new docs are great. One question I had while reading the whole middleware section is why does the middleware has to be curried? Why not simply:

var store = <..>;
store.addMiddleware(function(store, action, next) {
  var result = next();
  // whatever
});

Maybe a quick point explaining in the doc would be helpful.

@sergey-lapin
Copy link

Could be both actually ramda works that way.

@gaearon
Copy link
Contributor

gaearon commented Aug 17, 2015

Sometimes we want to associate some local state with store and next, like in rAF scheduler from middleware docs:

const rafScheduler = store => next => {
  let queuedActions = [];
  let frame = null;

  function loop() {
    frame = null;
    try {
      if (queuedActions.length) {
        next(queuedActions.shift());
      }
    } finally {
      maybeRaf();
    }
  }

  function maybeRaf() {
    if (queuedActions.length && !frame) {
      frame = requestAnimationFrame(loop);
    }
  }

  return action => {
    if (!action.meta || !action.meta.raf) {
      return next(action);
    }

    queuedActions.push(action);
    maybeRaf();

    return function cancel() {
      queuedActions = queuedActions.filter(a => a !== action)
    };
  };
};

We could have made it (store, next) => action => () but I don't see a problem with just going all the way. You might want some configuration later, at which point options => (store, next) => action => () looks kinda arbitrary.

@gaearon gaearon closed this as completed Aug 17, 2015
@kentor
Copy link
Contributor

kentor commented Sep 3, 2015

Honestly when I read the docs, specifically this line:

But there’s also a different way to enable chaining. The middleware could accept the next() dispatch function as a parameter instead of reading it from the store instance.

I had expected the following:

function logger(store, next) {]
  return function dispatchAndLog(action) {
    console.log('dispatching', action);
    let result = next(action);
    console.log('next state', store.getState());
    return result;
  };
}

But was instead:

function logger(store) {
  return function wrapDispatchToAddLogging(next) {
    return function dispatchAndLog(action) {
      console.log('dispatching', action);
      let result = next(action);
      console.log('next state', store.getState());
      return result;
    };
  }
}

I guess I am not satisfied with "I don't see a problem with just going all the way." To me, the problem is that it is neednessly complicated. The call to this in applyMiddleware

dispatch = middleware(store)(dispatch)

could simply be

dispatch = middleware(store, dispatch)

This also deviates from express' middleware signature: (req, res, next) => void. It isn't (req, res) => next => void.

@gaearon
Copy link
Contributor

gaearon commented Sep 3, 2015

To me, the problem is that it is neednessly complicated.

Is

store => next => action => yourcode

more complicated to write than

(store, next) => action => yourcode

?

I understand your concerns, maybe you're right. Maybe it breaks some use case we haven't considered. I don't know. But this feedback would have been more useful at the time middleware was designed (it was all publicly discussed: #55). Right now there is a lot of middleware in the ecosystem that uses the current signature, and unless you see a way to change it in backwards-compatible way, I don't think “remove store =>” is a reason compelling enough to break everyone.

@karthikiyengar
Copy link

Thanks for the question. I was starting to question my understanding of the middleware lesson by believing that the currying was absolutely necessary 😄

@ghost
Copy link

ghost commented Oct 25, 2018

I think that since the redux expectation of the function is this signature

const middleware = store => next => action => {
    // do something
}

then you can write your middleware curried version as follows:

_.curry((store, next, action) => {
   // do something
})

then redux being calling middleware(store)(next)(action) will work with the curried version.

@reduxjs reduxjs deleted a comment from ycjcl868 Oct 7, 2019
@reduxjs reduxjs locked as resolved and limited conversation to collaborators Oct 7, 2019
@markerikson
Copy link
Contributor

For future reference, this is covered in the Redux FAQ entry on "why does the middleware signature use currying?".

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants