So I've just read up on redux middleware, and it sounds great. One thing is bugging me though - the return values of the middleware.
I understand that some instances of middleware return stuff (i.e. redux-promise
) , and I get that other middleware (i.e. logging
) doesn't - and simply returns the result of next(action)
.
My issue is what happens if I want to use two pieces of middleware that both return stuff - surely they will clobber each other and I will only get the outer-most middleware's return value.
express/connect
middleware addresses this by letting middleware write its "results" to the req
and res
objects, but whats the solution with redux?
EDIT
Here is a more concrete example of my issue:
I have two pieces of middleware:
Depending on the order I chain these two bits of middleware, the result of my dispatch(action)
will either be the defer cancellation fn, or the number 5. But how do I get both of these results?
This middleware returns a function that can be called to cancel the dispatch.
Redux Thunk is recommended middleware that helps you to overcome the side effects of basic Redux. Thunk Redux helps you solve complex synchronous logic as well as simple async logic. Let me quote a redux-thunk example of synchronous logic- it can be access to the store.
Redux Async Data Flow Just like with a normal action, we first need to handle a user event in the application, such as a click on a button. Then, we call dispatch() , and pass in something, whether it be a plain action object, a function, or some other value that a middleware can look for.
Dispatching an action within a reducer is an anti-pattern. Your reducer should be without side effects, simply digesting the action payload and returning a new state object. Adding listeners and dispatching actions within the reducer can lead to chained actions and other side effects.
So below is a runnable script that demonstrates the problem I'm trying (and failing) to describe. It also includes a potential solution (using a middleware wrapper). Would love to know if there are any more elegant solutions out there....
var { createStore, applyMiddleware } = require( "redux" );
var dispatchResult;
// create the results object to be passed along the middleware chain, collecting
// results as it goes
const genesis = _store => next => action => {
next( action );
return {};
};
const wrapper = ( key, mware ) => store => next => action => {
// extract the results object by storing the result of next(action)
// when it is called within the middleware
var extractedResult;
function modifiedNext( action ) {
extractedResult = next( action );
return extractedResult;
}
// get the result of this middleware and append it to the results object
// then pass on said results object...
var newResult = mware( store )( modifiedNext )( action );
extractedResult[ key ] = newResult;
return extractedResult;
};
// create standard logging middleware
const logger = store => next => action => {
let result = next( action );
console.log( `value is: ${ store.getState() }.`);
return result;
};
// create middleware that returns a number
const gimme = val => _store => next => action => {
next( action );
return val;
};
// create our super simple counter incrementer reduer
function reducer( state = 0, action ) {
if( action.type === "INC" )
return state + 1;
return state;
}
// first lets try running this without the wrapper:
dispatchResult = createStore( reducer, applyMiddleware(
gimme( 4 ),
logger,
gimme( 5 )
) ).dispatch( { type : "INC" } );
// will return only 4 (the result of the outermost middleware)
// we have lost the 5 from the gimme(5) middleware
console.log( dispatchResult );
// now we include the middleware wrapper and genesis middleware
dispatchResult = createStore( reducer, applyMiddleware(
wrapper( "g4", gimme( 4 ) ),
logger,
wrapper( "g5", gimme( 5 ) ),
genesis
) ).dispatch( { type : "INC" } );
// we will now return { g4 : 4, g5 : 5 }
// we have preserved the results of both middlewares
console.log( dispatchResult );
Take a look at the documentation on applyMiddleware
. It explains that middlewares are to be written to be composable, so that it can be plugged into the chain of middlewares without worrying about the middlewares that are applied before and after it:
The key feature of middleware is that it is composable. Multiple middleware can be combined together, where each middleware requires no knowledge of what comes before or after it in the chain.
The documentation does a very good job of explaining the arguments that are to be passed into the middleware and the expected return.
https://redux.js.org/api/applyMiddleware
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With