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.
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.
According to the transcript, Dan describes the pattern of a function returning a function as 'Korean' although he almost certainly means to say 'currying.'
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...
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.
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.
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.
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."
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.