Redux Middleware understanding guidance - redux

const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(thunk, api, wsMiddleware, createLogger()),
typeof window === 'object' && typeof window.devToolsExtension !==
'undefined'
? window.devToolsExtension()
: DevTools.instrument(),
),
);
So the above is how you would usually create a store, and then you have your middleware that starts like this:
export default store => next => (action) => {
I did read up the middleware portion from redux.org, but anyone able to better explain it to me what's going on there ?
so is the middleware is receiving the store ? and calling out the next function from the store and than finally using the parameters given as action (in this case). ?

The Redux middleware pipeline can be broken down like this...
store => {...}
The store API is the first argument given to the pipeline. This allows the middleware to get the current state at any point in the pipeline and/or dispatch new actions into the store.
Note: It has many of the same characteristics as the store returned from the createStore function, but it is not the same. Only the dispatch and getState functions are available.
next => {...}
The next argument is a reference to the next middleware in the chain. If there are no more middleware to go, the store handles the action (i.e. pass it into the the reducer).
If next is not called, the action will not make it to the reducer. This can be useful to suppressing something that is not a valid action on it's own, such as a function or a promise, as an error will be raised by Redux if it tries to handle it.
action => {...}
The action argument is the thing that gets dispatched into the store.
{...}
In here is where you will test the action to see if there is something special you want to do with it and whether you will pass it onto the next handler.
An example
For this example, we will create a simplified thunk middleware, explaining how it uses each part of the pipeline.
export default store => next => action => {
// if the action is a function, treat it as a thunk
if (typeof action === 'function') {
// give the store's dispatch and getState function to the thunk
// we want any actions dispatched by the thunk to go through the
// whole pipeline, so we use the store API dispatch instead of next
return action(store.dispatch, store.getState)
} else {
// we're not handling it, so let the next handler have a go
return next(action)
}
}

Functions that conform to the Redux middleware API. Each middleware receives Store's dispatch and getState functions as named arguments, and returns a function. That function will be given the next middleware's dispatch method, and is expected to return a function of action calling next(action) with a potentially different argument, or at a different time, or maybe not calling it at all. The last middleware in the chain will receive the real store's dispatch method as the next parameter, thus ending the chain. So, the middleware signature is ({ getState, dispatch }) => next => action.
The answer was found in the applymiddleware documentation.
https://redux.js.org/docs/api/applyMiddleware.html

Related

How to have a Subject as a payload in an immutable Ngrx action?

Use case: dispatch an action with a cold observable in the payload.
When an effect catches the action, it subscribes (through mergeMap, switchMap, whatever...) to this observable and send back another action. Classic Ngrx process.
export class ServicesStore {
dispatchObservable(operation$: Observable<unknown>) {
this.store.dispatch(serviceRequestAction({ operation$ }));
}
}
export class ServicesEffects {
serviceRequest$ = createEffect(() =>
this.actions$.pipe(
ofType(serviceRequestAction),
mergeMap((action: ServiceRequestAction) => {
return action.operation$.pipe(
map((result) => {
// send back an action with the result
})
);
})
)
);
}
Usage:
this.servicesStore.dispatch(this.userService.getAll$());
It works well.
But if this observable is a Subject (say MatDialog.open().afterClosed()) it will break the immutable action Ngrx rule.
Because of the inner subscription, the Subject adds an observer into its internal structure, thus breaking the action immutability. It then triggers the Ngrx runtime checks.
Of course I can disable these check, but I am looking for a better away around this. For example, is there a way to clone a Subject ?
Or any other way to allow a Subject into the action payload ?
AFAIK adding a subject to a NgRx Action isn't supported (if you want to keep the runtime checks enabled).
The classic NgRx process is that the effect results in a new action (popular ones are success and failure).

How to apply middleware selectively to reducers?

With reduxjs-toolkit I define my store and middleware something like this:
const store = configureStore({
reducer: rootReducer,
middleware: [reduxWebsocketMiddleware, adamBrownMiddleware]
});
However I only want to apply this middleware to certain reducers, not all. Specificially I want to apply websocket middleware to certain reducers, whereas in others I'm calling an API and this websocket middleware is not necessary.
It seems like adding another store to do this is an anti-pattern.
So how can I apply middleware only for certain reducers?
You can always, in your middleware, check the action.type property (for example if it begins with "mySliceName/") and otherwise skip the rest of the middleware.
As you don't really give an example what your middleware is doing and why you want to limit it, it's not really possible to give you any more input than that.
This could look like this:
const middleware = api => next => action => {
if (!action.type.startsWith("myWebsocketSlice/") { return next(action); }
// normal middleware code here
}

Does applying Redux-Thunk midleware to redux change all actions to be executed asynchronously?

I have a project in which I have applied the redux-thunk middleware to my redux store. Now I have several thunks in my code and this are also being dispatched, but they return ThunkAction<Promise<void>, void, void, AnyAction> so I assume these are async.
In other places of my code I’m directly calling the dispatch method on the store. Does this actions also become asynchronous as an effect of applying middleware or do they remain synchronous?
For example If i do:
store.dispatch(someAction);
Would that still be synchronous?
Thanks in advance.
Yes.
Dispatching is 100% synchronous by default.
If you add middleware, a middleware may alter, delay, or stop an action from progressing through the dispatch pipeline.
However, in this case, the redux-thunk middleware does not do anything asynchronous. It simply checks to see if the "action" is actually a function, and if so, executes it immediately:
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => (next) => (action) => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
If the thunk middleware is applied, store.dispatch(someThunkFunction) will return whatever the thunk function returns. So, if you return a promise from the thunk, that promise will be returned from store.dispatch().
For the TS types, the first generic argument should indicate the expected return type of the thunk function. However, you would likely actually have to specify that yourself - it shouldn't be appearing randomly.

mix redux-thunk with redux-observable

I have an app using redux-thunk and want to try out redux-observable.
Currently I have a thunk action creator that does the following:
const initState = () => {
return dispatch => new Promise((resolve,reject)=>{
//these each return axios promises as they get data from the server
const pr1 = dispatch(processData1())
const pr2 = dispatch(processData2())
Promise.all([pr1,pr2])
.then(r=>resolve(true))
.catch(e=>reject(e))
})
}
}
and this is called from my component in componentDidMount like so:
this.props.initState()
.then(r=>this.setState({loaded:true}))
.catch(e=>this.setState({loadError:true}))
Now I want to try redux-observable Can I combine it with redux-thunk and replace the processData2() call to instead dispatch an action which is dealt with by redux-observable?
i.e.
const pr2 = dispatch({type:OBSERVABLE_PROCESSDATA2})
Now as far as I can tell, this isn't going to return a promise so the original code will now break as in Promise.all will .then(r=>resolve(true)) immediately even though redux-observable epic has not yet processed it.
So is there a way I can wait for that dispatch to do the async stuff in the epic or do I need to basically go redux-observable all the way in this case?
You can combine redux-thunk and redux-observable in the same project.
actions ---> redux-thunk ---> redux-observable ---> store
But the idea of redux-observable is action$ in and action$ out. So you will need to write all your async logic in epics, and don't have access to the returned observable which is action stream.
So it's either you may want to convert the whole initState to an epic or just use rx.js directly in this thunk without creating epics

Redux Middleware currying

I have this question in my head, not sure if this is validate or not, below it's an example of redux middle console log out the store.
const logger = store => next => action => {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
I can see it's using currying, so in redux is calling as logger(store)(store.dispatch)(action) (Correct me if i am wrong). My question is why we currying here instead just
(store, next, action) => { // do the rest }
Thanks for any suggestion I am slowly moving into functional programming too to get my head up rhythm with it.
I think redux wants to provide three hooks to developers.
We can split the call chain logger(store)(next)(action) into
let haveStoreAndDispatch = logger(store);
let haveNext = haveStoreAndDispatch(next);
let haveAction = haveNext(action);
Then we get three hook functions.
In haveStoreAndDispatch callback function, store have been created.
In haveNext callback function, we have get the next middleware.
In HaveAction callback function, we can do something with the previous middleware's result action.
Callbacks haveStoreAndDispatch and haveNext just be called only once in applyMiddleware(...middlewares)(createStore).

Resources