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.
Could be both actually ramda works that way.
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.
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.
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.
Thanks for the question. I was starting to question my understanding of the middleware lesson by believing that the currying was absolutely necessary :smile:
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.
For future reference, this is covered in the Redux FAQ entry on "why does the middleware signature use currying?".
Most helpful comment
Thanks for the question. I was starting to question my understanding of the middleware lesson by believing that the currying was absolutely necessary :smile: