The main introduction of the createstore,combinereducers,compose of the implementation of the principle, below, we look at the redux of the most interesting middleware part of the applymiddleware.
Applymiddleware code is concise, but it has a broad meaning. Let's take a look at:
First, let's revisit the middleware approach: invoking middleware
Take a look at CreateStore's source code.
if (typeof preloadedstate = = = ' function ' && typeof enhancer = = ' undefined ') {
enhancer = Preloadedstate
P reloadedstate = undefined
}
//enhancer must be a higher order function
if (typeof enhancer!== ' undefined ') {
if (typeof Enhancer!== ' function ') {
throw new Error (' expected the enhancer to be a function. ')
}
Intensifier return
Enhancer (createstore) (reducer, preloadedstate)
}
Enhancer is a middleware that can also be set directly on the second parameter when the second parameter of CreateStore is a function and there is no third parameter. Then there are two ways to set up the middleware
CreateStore (Reducer,initstate,applymiddleware (Thunkmiddleware));
Or
CreateStore (Reducer,applymiddleware (Thunkmiddleware));
After familiarity with usage, let's take a look at how Applymiddleware is implemented:
Export default function Applymiddleware (... middlewares) {return (CreateStore) => (reducer, preloadedstate, enhancer) => {//Generate a store const store = createstore (reducer, preloadedstate, enhancer) Let dispatch = Store.dispa
tch Let chain = []//rough version of the store, which contains the ' getState ' and ' dispatch ' two methods. Const MIDDLEWAREAPI = {GetState:store.getState, dispatch: (Action) => Dispatch (Action)}//Will MIDDL
Ewareapi is injected into each middleware as a parameter, executes the middleware, and returns a new chain.
Middleware functions (Store) =>next=>action.
Chain [Next=>action,...]; This next is actually store.dispatch. And the ' action ' is the ' dispatch ' action chain = middlewares.map (middleware => Middleware (MIDDLEWAREAPI))//We assume there are three middleware, fn1
, Fn2,fn3, then the following code is equivalent to DISPATCH=FN1 (FN2 (Fn3 (Store.dispatch));
It can be found that the dispatch of middleware is actually a function that performs fn1,fn2,fn3.
So each middleware, when confronted with an action that is not within its own scope, uses the next action to pass it to the next middleware.
dispatch = Compose (... chain) (Store.dispatch) return {... store, dispatch }
}
}
Another piece of Redux-thunk middleware source code as a reference, together to see
function Createthunkmiddleware (extraargument) {return
({dispatch, getState}) => next => action => {
if (typeof action = = ' function ') {return
action (dispatch, GetState, extraargument);
}
Return next (action);}
We start with the middleware code, a little bit of profiling applymiddleware.
There is a piece of code in the Middleware code: ({dispatch,getstate}) =>next=>action=>{};
Combined with the above Applymiddleware source to see, it is easy to find ({dispatch,getstate}) refers to the Middlewareapi, is a humble version of the store, and action is our dispatch action. So what's this next?
When executing chain = MIDDLEWARES.MAP (middleware => Middleware (MIDDLEWAREAPI)), we pass MIDDLEWAREAPI as arguments, corresponding to the middleware code ({ Dispatch,getstate}) => this step. The chain that is executed represents [(next) =>action=>{},...].
Keep looking down,
dispatch = Compose (... chain) (Store.dispatch)
At this point, we can find that the next parameter is actually the original store.dispatch of this dispatch.
So, applymiddleware in the multi-middleware scenario, how it works .
Let's take a look at dispatch = Compose (... chain) (Store.dispatch).
Assuming we have fn1,fn2,fn3 three middleware, the above functions can be disassembled as
Dispatch=fn1 (Fn2 (Fn3 (Store.dispatch)));
Let us give you an example:
Const A = next => () => {
Console.log (' a pre ');
Next (); Implementation B (C (d ()));
Console.log (' a after ');
}
Const B = Next => () => {
Console.log (' b pre ');
Next (); Implementation of C (d ());
Console.log (' b after ');
}
Const C = Next => () => {
console.log (' C pre ');
Next (); Execute D ();
Console.log (' c after ');
}
Const D = () => {
console.log (' Hello World ');
}
function Compose (... funcs) {return
funcs.reduce ((A, B) => (... args) => a (b (... args)))
}
Console.log (Compose (a, B, c) (d) ())
Open the console of the browser to find that the execution result is
A pre
B pre
C pre
Hello world
c after
a
By compose function, the Compose (A,b,c) (d) () is synthesized into a (b (c (d))) ();
You first execute a () and then print out a pre on the console, then execute next (), where next is actually B (c (d)), then execute B (), print out the B pre, then execute next (), at which point next indicates C (d), then execute C (), and print out C Pre, followed by Next (), where next represents D, then executes D () and prints out the HelloWorld.
Then continue to execute, print out C after, then B after,a after.
This example is a good explanation for the process of middleware implementation. Our compose function only makes the FN1,FN2,FN3 a fn1 (fn2 (FN3)) function, does not run the innermost function immediately, only then executes the FN1 after the fn2 executes next, likewise, FN2 executes only after the FN3 executes the next. Analogy If we don't execute next at a certain level, then the chain is broken.
So, when we use Redux-logger this middleware, we have to put it on the middlewares to the far right, because we are worried that a later middleware in case of not executing next, the whole chain is broken, so even if he executed next, it is useless.
This also requires us to perform a next action when writing middleware, because as long as each middleware executes the next (action) routine, it guarantees that the chain will continue, and the original dispatch can be distributed all the time.