Redux: The Middleware Chain

Share this video with your friends

Send Tweet

We will learn how we can generalize wrapping dispatch() for different purposes into a concept called “middleware” that is widely available in the Redux ecosystem.

Ningze
Ningze
~ 8 years ago

what is the point of having store as the first parameter in the curry function? I didn't see anywhere you use the store in the logger or promise function.

Ron Keiser
Ron Keiser
~ 8 years ago

According to the transcript, Dan describes the pattern of a function returning a function as 'Korean' although he almost certainly means to say 'currying.'

mobility-team
mobility-team
~ 8 years ago

When you'll reach video 23, you'll get it :) I think he writes it this way, because he knows he'll need it later...

Brickwork
Brickwork
~ 7 years ago

Im not sure if this was a mistake or something that I'm missing in the code that's being explained but around 6:40 you describe that the middlewares are passed in the reverse order that they are being defined but from what I can see (and I ran this through the debugger,) since the promise function is defined first and after the array is reversed, is therefore sent through last. In the video you're saying that the logger is sent last. Although this logically makes sense to me it seems as though its not functioning this way since the logger action would be undefined as demonstrated in an earlier video.

Sami
Sami
~ 6 years ago

When you'll reach video 23, you'll get it :) I think he writes it this way, because he knows he'll need it later...

Heh ok thanks for expressing that. In the current examples, it seems clear that currying is unnecessary and we could just pass multiple arguments into one function. Thanks for your comment. :) I'll carry on now knowing this is going to be useful later.

J. Matthew
J. Matthew
~ 5 years ago

what is the point of having store as the first parameter in the curry function? I didn't see anywhere you use the store in the logger or promise function.

store is actually being used in logger, to call getState() so the state can be logged.

With that in mind, it makes sense to pass it to promise as well, even though the latter doesn't use it, just so that the two functions have the same signature, and can therefore be used in wrapDispatchWithMiddlewares.

I imagine that it's a standard pattern when writing middleware to include the store so that the middleware has the option of doing something with it.

J. Matthew
J. Matthew
~ 5 years ago

I agree with @Sami that the currying isn't necessary in this specific example. We could just as well replace store => next => with (store, next) =>, and middleware(store)(store.dispatch) with middleware(store, store.dispatch).

For that matter, since we're currently passing as the second argument the dispatch property of the first argument, we could even go back to the earlier code and not pass dispatch as an argument at all, but rather extract it from store within the function.

While I think the lesson on the whole is excellent, the currying strikes me as premature and unnecessarily confusing. Dan offers something like the following as a brief justification (from the transcript): "Again, rather than take the next middleware from the store, I will make it intractable as an argument so that my function that calls the middlewares can choose which middleware to pass." I was confused as much by this explanation as by the code it's explaining, but I think it means that there might be a scenario where you want to pass one store, but then a dispatch that is different than the one attached to that store. Extracting dispatch from store within the function would no longer work for that purpose. However, I don't think that such a scenario is actually occurring in this lesson, so there's nothing to illustrate why this is worth doing.

And, even in such a scenario, you could still achieve the same end by passing both store and whatever other dispatch as two arguments to the same function, like the code in my first paragraph. I'm still wrapping my head around the intricacies of functional programming (and specifically functions that return functions), so there may well be some benefit of doing it with currying in this specific example that I'm not seeing. But that just further emphasizes that the technique in this lesson outstrips the need and the explanations given.

If indeed this will pay off in later lessons, as @mobility-team suggests, I would rather Dan have saved writing the code until then, when its utility could be illustrated. Or, at bare minimum, say in this lesson "don't worry about why I'm writing this this way; it will be useful later."

J. Matthew
J. Matthew
~ 5 years ago

Im not sure if this was a mistake or something that I'm missing in the code that's being explained but around 6:40 you describe that the middlewares are passed in the reverse order that they are being defined but from what I can see (and I ran this through the debugger,) since the promise function is defined first and after the array is reversed, is therefore sent through last. In the video you're saying that the logger is sent last. Although this logically makes sense to me it seems as though its not functioning this way since the logger action would be undefined as demonstrated in an earlier video.

@Brickwork I totally relate to your confusion, because, while I believe what Dan said is correct, it's a bit mind-bending to think through. In a way, the chronology reverses itself twice(!). It works like this (three steps):

Step 1—Populate the middlewares array: We place promise in the empty array and then logger after it. So promise is "defined" first in the array, and logger last.

Step 2—Apply the middlewares to store.dispatch: We iterate through the middlewares array, in reverse, overriding store.dispatch with the result of calling the middleware on store. So we call logger first and promise last.

However, think about what actually happens when you call each middleware on the store. It wraps whatever store.dispatch is currently with some other code that will run beforehand. So when we call logger first, the result is that calling store.dispatch will run logger's custom code, then the code of the original store.dispatch that came with Redux. And then when we call promise, the result is that calling store.dispatch will run promise's custom code, then logger's custom code, then the code of the original store.dispatch that came with Redux.

The critical thing to grasp here is that each successive middleware wraps the previous ones, so an entry in the array that gets applied to store after another will run its code before the other.

Step 3—Dispatch an action: After configuring store is complete, any action we dispatch will get passed through all the middlewares. So in our example, dispatching an action will run it through promise first, then logger, then Redux's default dispatch.

This is why Dan says that the middlewares are 1) "defined" in one order (promise added to the array before logger) but then 2) "passed" in the reverse order (logger applied to store.dispatch before promise) but then 3) "sent" in the original order (a dispatched action is processed first by promise then by logger)—the "double reversal" I mentioned at the top.

Why do it this way? We could have added logger to the array first and then promise, and then not reversed the array when applying the middleware. That would keep the middlewares "defined" in the same order that they're "passed." However, it would still be the case that, because each applied middleware wraps the previous one, an action would be "sent" (i.e. processed by the middlewares) in the reverse order, promise before logger.

That would only be a single reversal, which maybe is easier to understand. But then you have to remember to add the middlewares to the array in the reverse order that you want them to process actions. That is, you have to keep in mind that even though you put logger in the array first, promise will actually take effect first. To me this seems more confusing in the long term, since you always have to remember this. I think that's why Dan writes it the other way—yes, it's confusing to understand the code initially, but then once you do, you can just add middlewares to the array in the same order that you want them to take effect, and that's much easier to understand.