React Redux middleware's #store.dispatch(action) vs #next(action) - javascript

I'm learning about react redux middleware from the docs and want to know what he means by "the action will actually travel the whole middleware chain again?" Does that mean the store.dispatch(action) will have the final dispatch that is returned at the end of the middleware, whereas next(action) will only point to the dispatch at a certain point of the middleware function?
It does a bit of trickery to make sure that if you call
store.dispatch(action) from your middleware instead of next(action),
the action will actually travel the whole middleware chain again,
including the current middleware. This is useful for asynchronous
middleware, as we have seen previously.

In redux middleware is a function that patches dispatch function to extend functionality. And next function inside middleware is in fact just a copy of store.dispatch function.
function patchStoreToAddLogging(store) {
let next = store.dispatch
store.dispatch = function dispatchAndLog(action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
}
So when you return next(action) you return a result of dispatch function, with all previous middlewares in the chain applied, to the next middleware.
But when you call store.dispatch function you call a final dispatch method return from middleware chain with all middlewares applied.

Related

How do I make two dispatches in redux run in order?

Suppose I have two dispatches I want to fire in order called
dispatch(action1())
dispatch(action2())
Action1 is an action creator created using
createAsyncThunk
method from redux/toolkit.
Therefore, it uses async... await in the process.
Action2 is a synchronous process.
I want to make
`dispatch(action2())` run only after `action1()` has been dispatched.
How can I achieve this?
I have tried doing
dispatch(action1(), dispatch(action2())
but I have found that dispatching inside of a reducer is an anti-pattern since reducers are pure.
See Unwrapping Result Actions
Thunks may return a value when dispatched. A common use case is to return a promise from the thunk, dispatch the thunk from a component, and then wait for the promise to resolve before doing additional work:
So you can do this:
dispatch(action1()).then(() => {
dispatch(action2());
})
A simple way might be to let action1 conditionally dispatch action2 where you need it:
const action1 = createAsyncThunk(
'actions/1',
async (args, thunkAPI) => {
const response = await someApi.fetchStuff(args.id);
if (args.condition) {
thunkAPI.dispatch(action2());
}
return response.data
}
);
This only works if you don't depend on the reducer having updated the state at that point (usually with this type of scenario it's about the asynchronous work having finished).
If you however also depend on the state update, try the dispatch(action1()).then(...) answer someone else gave.
Another option is to write a "classic" thunk (not using createAsyncThunk) that coordinates the sequence of action1 and action2.
I personally prefer the last option since bigger thunks can be composed nicely of many smaller thunks.
In case you're wondering, createAsyncThunk is not meant to dispatch another action when returning its result - which would have also solved your problem: https://github.com/reduxjs/redux-toolkit/issues/773

Redux - Calling storeAPI.disapatch(action) inside middleware causes 'too much recursion'

I'm working through the Redux fundamentals tutorial. In the section on 'Writing Custom Middleware', we learn that middleware are written as a series of three nested functions like so:
// Outer function:
function exampleMiddleware(storeAPI) {
return function wrapDispatch(next) {
return function handleAction(action) {
// Do anything here: pass the action onwards with next(action),
// or restart the pipeline with storeAPI.dispatch(action)
// Can also use storeAPI.getState() here
return next(action)
}
}
}
exampleMiddleware is explained as follows:
exampleMiddleware: The outer function is actually the "middleware"
itself. It will be called by applyMiddleware, and receives a storeAPI
object containing the store's {dispatch, getState} functions. These
are the same dispatch and getState functions that are actually part of
the store. If you call this dispatch function, it will send the action
to the start of the middleware pipeline. This is only called once.
I didn't understand what was meant by the second last sentence (If you call this dispatch function, it will send the action to the start of the middleware pipeline), so I tried calling store.dispatch(action) inside one of the middlewares provided in src/exampleAddons/middleware.js of the example app to see what happens and got "too much recursion". Here's the demo.
So storeAPI.dispatch() is the composed dispatch function of all the middlewares combined rather than the original store's dispatch, which would explain the recursion. But then what is the use of storeAPI.dispatch()? Am I using it incorrectly?
In applyMiddleware's source:
function applyMiddleware(...middlewares) {
return createStore => (...args) => {
// ...1) createStore is called and the resulting store is saved as `store`
const store = createStore(...args)
// ...2) a `dispatch` variable is defined and assigned some function
let dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
// ...3) a middlewareAPI object is defined containing the store's getState method and the `dispatch` function from 2).
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
// ...4) the middlewares passed to applyMiddleware are called with the `middlewareAPI` object from 3) and the resulting functions are saved in array `chain`.
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// ...5) the middlewares are composed and the resulting "composed" middleware function is called with `store.dispatch`.
// This returns a composed dispatch function that chains together the `handleAction` functions of all the middlewares passed to applyMiddleware.
// This composed dispatch gets assigned to the `dispatch` variable from 2).
// Since the `storeAPI.dispatch` is referencing this variable, calling `storeAPI.dispatch` now calls the composed middleware function, which causes the infinite loop.
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
The infinite loop seems to be the result of the re-assignment at step 5 in the annotations above. But I'm not sure if my reasoning is correct or if I'm even using storeAPI.dispatch correctly. I would appreciate any guidance the community could provide here as I wasn't able to find any examples of middleware that call storeAPI.dispatch().
Yes, calling storeAPI.dispatch() in a middleware sends the action to the very start of the middleware pipeline. That means that if we have middlewares a->b->c->store, and b calls storeAPI.dispatch({type: "some/action"}), middleware b will see that exact same action object go through almost immediately.
Because of that, a middleware should never call storeAPI.dispatch() unconditionally, because that will cause infinite loops! This is basically the same problem as something like calling setState() unconditionally in a React component useEffect hook. The effect runs after rendering, and setState() queues up another render, so if you always set state every time, you always force a re-render, and that's an infinite loop. Same thing here.
So, any use of storeAPI.dispatch() in a middleware should be wrapped in a conditional check so that it only happens some of the time, not all of the time.
But then what is the use of storeAPI.dispatch()?
Let's examine the naive implementation of thunk middleware. The middleware basically allows dispatch to receive a function (called dispatch function) as its argument (which is normally a plain action object).
const asyncFunctionMiddleware = store => next => action => {
if (typeof action === 'function') {
return action(store.dispatch, store.getState); // (*)
}
return next(action);
}
Async logic will go inside the dispatch function which has been provided with two essential arguments: dispatch and getState - See line (*).
These two arguments are sufficient for developers to interact with the redux store in their async logic. And for the intended use-case, developer's will is to be able to dispatch an action as a normal operation (with all the provided middleware feature). Therefore, a store.dispatch is used here in the implementation.
What happen if we passed next instead?
Depend on the position of the thunk middleware in the middleware list, the next will be the next wrapped dispatch object followed by thunk middleware.
Imagine the order is a->thunk->b->store. Then dispatch using next in thunk will give your no effect of middleware a on the action.
This behavior isn't desired in the thunk use-case, hence using store.dispatch here is appropriate.
Therefore, thunk is a situation that we should make use of store.dispatch in middleware

Why does redux thunk return promise?

I am learning redux-thunk middleware as a beginner react developper, and I don't understand why dos the function (returned by redux-thunk) returns a promise (returned by fetch())
I tried not to return anything, and it worked, so why do we return it?
export function getCourses() {
return fetch(baseUrl)
.then(handleResponse)
.catch(handleError);
}
export function loadCourses() {
return function(dispatch) {
dispatch(beginApiCall());// dispatch some synchronous action
return courseApi
.getCourses().then(courses => {
dispatch(loadCourseSuccess(courses));
}).catch(error => {throw error;});
};
}
For the component named MyComponent dispatching loadCourses() action
function MyComponent(props){
.......
useEffect(() => {
loadCourses()
});
const mapDispatchToProps = {
loadCourses,
.....
}
}
I think i got the answer(from a colleague).
if you want to chain certain activities, your action would need to return a Promise.
it's just a good way to allow chaining activities after the result is returned!
Well, first of all the function returns some data because you asked it to return some sort of result return function(dispatch) {...}.
If you want to ignore the returned result just remove the return from return function(dispatch) {...}.
Secondly, the function returns a promise because of the way that you have written your call to API functions (wrapped inside promise and not returning callbacks upon function completion).
If you want to get the actual result of the API call you should use the Async / Await syntax.
With a plain basic Redux store, you can only do simple synchronous updates by dispatching an action. Middleware extends the store's abilities and let you write async logic that interacts with the store.
Thunks are the recommended middleware for basic Redux side effects logic, including complex synchronous logic that needs access to the store, and simple async logic like AJAX requests.https://github.com/gaearon/redux-thunk
The thunk middleware knows how to turn thunk async actions into actions, so you just have to have your simple_action() to be a thunk and the thunk middleware will do the job for you, if the middleware see a normal action, he will dispatch this action as normal action but if it's an async function it will turn your async action into normal action.
You can also see return promise from store after redux thunk dispatch

Why use redux-thunk? [duplicate]

This question already has answers here:
Why do we need middleware for async flow in Redux?
(12 answers)
Closed 5 years ago.
I don't understand the need for something like redux-thunk. From what I understand a thunk is a function which returns a function. The wrapped expressions and the use of middleware appear to me to do more to obfuscate what is happening. Taken from redux-thunk's sample code
import thunk from 'redux-thunk';
// Note: this API requires redux#>=3.1.0
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
// Meet thunks.
// A thunk is a function t hat returns a function.
// This is a thunk.
function makeASandwichWithSecretSauce(forPerson) {
// Invert control!
// Return a function that accepts `dispatch` so we can dispatch later.
// Thunk middleware knows how to turn thunk async actions into actions.
return function (dispatch) {
return fetchSecretSauce().then(
sauce => dispatch(makeASandwich(forPerson, sauce)),
error => dispatch(apologize('The Sandwich Shop', forPerson, error))
);
};
}
// Thunk middleware lets me dispatch thunk async actions
// as if they were actions!
store.dispatch(
makeASandwichWithSecretSauce('Me')
);
The above code could be written far more concisely and intuitive:
fetchSecretSauce().then(
sauce => store.dispatch(makeASandwich('Me', sauce)),
error => store.dispatch(apologize('The Sandwich Shop', forPerson, error))
)
My question is what need is redux-thunk fulfilling and how does it improve on existing solutions similar to the example above.
Redux Thunk teaches Redux to recognize special kinds of actions that are in fact functions.
When an action creator returns a function, that function will get executed by the Redux Thunk middleware. This function doesn't need to be pure; it is thus allowed to have side effects, including executing asynchronous API calls. The function can also dispatch actions.
The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met.
If Redux Thunk middleware is enabled, any time you attempt to dispatch a function instead of an action object, the middleware will call that function with dispatch method itself as the first argument.
And then since we “taught” Redux to recognize such “special” action creators (we call them thunk action creators), we can now use them in any place where we would use regular action creators.
Check this great answer from Dan Abramov himself, it covers everything: https://stackoverflow.com/a/35415559/5714933
Also check these links for more info:
https://github.com/gaearon/redux-thunk#motivation
http://redux.js.org/docs/advanced/AsyncActions.html

What does it mean for an action to travel through an entire middleware chain in redux?

Looking at the react-redux docs, I don't get the below how about why having an action travel the whole middleware would be useful:
It does a bit of trickery to make sure that if you call
store.dispatch(action) from your middleware instead of next(action),
the action will actually travel the whole middleware chain again,
including the current middleware. This is useful for asynchronous
middleware, as we have seen previously.
What does it mean for an action to travel through the middleware? How does that affect the dispatch? My understanding was that the dispatch changes through each layer of middleware it goes through, and next refers to the previous middlware's dispatch, whereas dispatch refers to the original store.dispatch.
As you can see in the the middleware example, there are multiple middleware items that create a pipe:
rafScheduler -> timeoutScheduler -> thunk -> vanillaPromise -> etc...
An action travels all middleware items before getting to the base reducer or being intercepted by one of the middleware items. Each middleware item can decide to move an action to the next middleware in the chain by using next(). However, sometimes we want the action to travel the chain from the start.
For example, using redux thunk, we dispatch an async action, that will be handled by the thunk middleware. The async action will dispatch another action, when the async call succeeds. This action should start again with the rafScheduler middleware.
If dispatch would have worked like next, it would travel to the vanillaPromise middleware instead. To solve that, dispatch(action), no matter where it was called, always travels the chain from the start.
To create this behavior applyMiddleware() runs over the middleware store => next => action methods, passes the middlewareAPI api to the store param, passes, and overrides the store.dispatch, with a new dispatch that is the combined middleware. This is where the magic happens - the new dispatch is the chain of middleware methods, where each calls the one after it when next is invoked (next = the next middleware method), and the last ones next() is the old store.dispatch that calls the base reducer:
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
var store = createStore(reducer, preloadedState, enhancer)
var dispatch = store.dispatch // this is the original dispatch
var chain = []
/*** this the store param of the middleware ***/
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
/*** store param is applied to old middlewares to create the chain ***/
chain = middlewares.map(middleware => middleware(middlewareAPI))
/*** The chain is composed. For all methods in the chain, but the last, next() is the middleware method after it in the chain, the last next is store.dispatch ***/
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch // the new dispatch is returned instead of the old
}
}
}

Categories