Is it possible to await for dispatch is finish - reactjs

I have 2 actions, and I call from the first action to the second action .... and I need to wait until the second action is finished and only then continue with the action.
// first action
export const getDataFromAdmin = () => {
return async (dispatch, getState) => {
dispatch(getDeviceLocation());
console.log('only after getDeviceLocation is finsih');
AdminRepository.getDataFromAdminAndEnums(dispatch)
.then(adminData => {
//some code
})
.catch(error => {
console.log(`Splash Error = ${error.message}`);
});
};
};
//second action
export const getDeviceLocation = () => {
return async dispatch => {
dispatch({ type: actionsType.GET_DEVICE_LOCATION });
LocationManager.getCurrentPosition()
.then(location => {
dispatch({ type: actionsType.GET_DEVICE_LOCATION_SUCCESS });
})
.catch(error => {
dispatch({ type: actionsType.GET_DEVICE_LOCATION_ERROR, message: error.message });
});
};
};

No, it's not possible because dispatching an action is something like triggering action and action just invoke either middleware or reducer that's it. The action doesn't wait to complete the reducer or middleware. It just invokes and finishes his job.

Ok, so in the end I make async await by pass dispatch as a parmeter to the function.

Yes you can await dispatch, but only if you are using redux-thunk middleware.
dispatch is a synchronous action by default. redux-thunk middleware allows you to dispatch actions which are a function of dispatch, like the actions in your question, in addition to the standard { type: 'SOME_NAME' } action objects. redux-thunk also makes it so that dispatching these "thunk" actions is asyncronous. This allows you to use aysnc/await or Promise.then() when you call dispatch(myThunkAction());
async function someFunction() {
await dispatch(myThunkAction());
doSomethingElse();
}
dispatch(myThunkAction()).then(() => {
doSomethingElse();
});
Here is an example from the redux-thunk documentation:
// In fact I can write action creators that dispatch
// actions and async actions from other action creators,
// and I can build my control flow with Promises.
function makeSandwichesForEverybody() {
return function (dispatch, getState) {
if (!getState().sandwiches.isShopOpen) {
// You don’t have to return Promises, but it’s a handy convention
// so the caller can always call .then() on async dispatch result.
return Promise.resolve()
}
// We can dispatch both plain object actions and other thunks,
// which lets us compose the asynchronous actions in a single flow.
return dispatch(makeASandwichWithSecretSauce('My Grandma'))
.then(() =>
Promise.all([
dispatch(makeASandwichWithSecretSauce('Me')),
dispatch(makeASandwichWithSecretSauce('My wife'))
])
)
.then(() => dispatch(makeASandwichWithSecretSauce('Our kids')))
.then(() =>
dispatch(
getState().myMoney > 42
? withdrawMoney(42)
: apologize('Me', 'The Sandwich Shop')
)
)
}
}
You can view the complete code in the redux-thunk docs section on Composition to see how they define the makeASandwichWithSecretSauce thunk.
The resolved value of the Promise will be whatever you return in your myThunkAction function. Typically thunks will call dispatch and will not return anything, so this feature is rarely used.
Any return value from the inner function will be available as the return value of dispatch itself. This is convenient for orchestrating an asynchronous control flow with thunk action creators dispatching each other and returning Promises to wait for each other’s completion. (source)
async function someFunction() {
const result = await dispatch(myThunkAction());
doSomethingWithResult(result);
}
dispatch(myThunkAction()).then((result) => {
doSomethingWithResult(result);
});

Related

Redux Thunk vs Redux custom Middleware

I am new to redux. So after reading lots of tutorials.I understood that, redux need redux thunk to dispatch async actions by returning another function.But if I call http request inside custom middle-ware then
is it required redux thunk ?
is Redux custom middleware no side effects ? I mean no need to return another function.
If i use redux thunk then my action creator looks like this. This I understood
function incrementAsync() {
return (dispatch) => {
setTimeout(() => {
// Yay! Can invoke sync or async actions with `dispatch`
dispatch(increment());
}, 1000);
};
}
I have confusion in custom middle-ware.
https://blog.logrocket.com/managing-asynchronous-actions-in-redux-1bc7d28a00c6/
as per this blog
const httpMiddleware = store => next => action => {
if (action[HTTP_ACTION]) {
const actionInfo = action[HTTP_ACTION];
const fetchOptions = {
method: actionInfo.verb,
headers: actionInfo.headers,
body: actionInfo.payload || null
};
next({
type: actionInfo.type + "_REQUESTED"
});
fetch(actionInfo.endpoint, fetchOptions)
.then(response => response.json())
.then(data => next({
type: actionInfo.type + "_RECEIVED",
payload: data
}))
.catch(error => next({
type: actionInfo.type + "_FAILED",
payload: error
}));
} else {
return next(action);
}
}
they are not returning any dispatch function inside action. I know that store,next,action are the inner functions.
can any one help me to understand about this?
Thank you.
All redux-thunk is is a simple redux middleware that checks if your action is a function and acts accordingly. You can build it yourself in 5 minutes.
You can see that it deconstructs dispatch and getState from the store object, and then calls your action with them as parameters.
Have a look at it's source code.
So, your example code can look like this:
const httpMiddleware = store => next => action => {
if (action[HTTP_ACTION]) {
const actionInfo = action[HTTP_ACTION];
const fetchOptions = {
method: actionInfo.verb,
headers: actionInfo.headers,
body: actionInfo.payload || null
};
store.dispatch({
type: actionInfo.type + "_REQUESTED"
});
fetch(actionInfo.endpoint, fetchOptions)
.then(response => response.json())
.then(data => store.dispatch({
type: actionInfo.type + "_RECEIVED",
payload: data
}))
.catch(error => store.dispatch({
type: actionInfo.type + "_FAILED",
payload: error
}));
} else {
return next(action);
}
}
As you asked by point list I'm gonna reply by point list:
If i call http request inside custom middleware then is it required
redux thunk ?
Redux thunk is a middleware per se, it's recommended if you want to dispatch an action that make (let's say, for example) an AJAX call and dispatch two different action based on the result of that AJAX call, one if it fails and one if it succeeds.
is Redux custom middleware no side effects ? i mean no need to return another function.
If I'm understanding correctly: A middleware will simply take the action you dispatched and pass it through the chain, it'll let you do something before sending the action to the reducer "phase". What it'll do is entirely up to you. You need at least to do next(action) or block the action based on some logic, if needed.
Finally, a custom middleware like the one you posted is modifying the action you passed based on the response of an AJAX call, making it more of an "interceptor" than a simple middleware. Written this way, it's not dispatching a new action, it's more related to modifying the action you passed.
If you want to dispatch a new action based on the result of an ajax call, but without creating a middleware/interceptor, you could do it this way:
const postSomethingAction = postObject => {
return async dispatch => {
dispatch(postSomethingPending())
const response = await Api.postSomething(postObject)
if (response.message) {
dispatch(postSomethingError(response))
}
else {
dispatch(postSomethingSuccess(response.data))
}
}
}
In this example we're using Thunk do create an action that dispatch another action based on the result of Api.postSomething

React Redux - Waiting for async api call to finish before next action is dispatched

I am working on a web app that uses React + Redux, with a backend using Django (DRF). I am using axios to send in my API request, which is asynchronous. The issue I am facing right now is that the actions dispatched do not wait for the API call to finish before the next action is dispatched. Below is my code
const mapDispatchToProps = dispatch => ({
success: id => {
dispatch(fetchSalesProject(id));
dispatch(createMessage('Requirement successfully updated!'))
}
})
fetchSalesProject action (axiosInstance is just a custom modification of axios call, the functionality is the same)
export const fetchSalesProject = (id) => (dispatch) => {
console.log('enter sales project action')
axiosInstance
.get(`/sales-project/detail/${id}/`)
.then((res) => {
console.log('fetched data')
dispatch({
type: FETCH_SALES_PROJECT,
payload: res.data,
});
})
.catch((err) => dispatch(returnErrors(err.response.data, err.response.status)));
};
createMessage action
export const createMessage = (message) => {
console.log('message')
return {
type: CREATE_MESSAGE,
message: message,
};
};
When calling this.props.success (refer to mapDispatchToProps), the message is displayed before the api call response data is received (evident by the fact that console.log('message') runs before console.log('fetched data'))
I would want the data to be fetched from the api call before i run the createMessage action, is there any advise on how to accomplish that? I am new to React and especially Redux, so hope that you guys can point me in the right direction on how to accomplish that.
Also, can I check whether it is wrong to have a dispatch in the mapDispatchToProps, and also a dispatch within the action (refer to fetchSalesProject action). Would it cause any issues with performance or is it frowned upon to do so? Please advise me as I am quite confused with Redux.
Thanks all for reading through, all help is appreciated :-)
while you are dispatching from UI, you just sending an object towards reducer which in his turn will modify the state at the store and in the end of the process will re-render components that refer to props that changed. At the moment you are dispatching the first action, there is nothing that tells the component that it should wait before sending the next object to the reducer
So you have 2 options,
the first is at UI itself use componentDidUpdate or useEffect for run the second action after the first action reduced
componentDidUpdate(prevProps) {
if (prevProps.salesProject != this.props.salesProject)
dispatch(createMessage('Requirement successfully updated!'))
}
while I assume that dispatch(fetchSalesProject(id)); modify salesProject prop
Another way to do that, and in case you actually fine with that message and salesProject will gonna get together to the reducer, is to dispatch them in one action
export const fetchSalesProjectAndMessage = (id, message) => (dispatch) => {
console.log('enter sales project action')
axiosInstance
.get(`/sales-project/detail/${id}/`)
.then((res) => {
console.log('fetched data')
dispatch({
type: FETCH_SALES_PROJECT_AND_MESSAGE,
payload: { data: res.data, message }
});
})
.catch((err) => dispatch(returnErrors(err.response.data, err.response.status)));
};
and at reducer payload.data either payload.message will refer to desired info
There is a better way of doing this that does not force you combine two action creators. When your thunk action creator returns a promise then you can wait for it. Your current code did not return the axios promise but if you do return it you can do the following:\
const mapDispatchToProps = (dispatch) => ({
success: (id) => {
dispatch(fetchSalesProject(id)).then(() =>
dispatch(//wait for fetchSalesProject to finish
createMessage('Requirement successfully updated!')
)
);
},
});
export const fetchSalesProject = (id) => (dispatch) => {
console.log('enter sales project action');
return axiosInstance //you did not return anything here
.get(`/sales-project/detail/${id}/`)
.then((res) => {
console.log('fetched data');
dispatch({
type: FETCH_SALES_PROJECT,
payload: res.data,
});
})
.catch((err) => {
dispatch(
returnErrors(err.response.data, err.response.status)
);
//return rejected promise here
return Promise.reject(err);
});
};

Callback function for this.props.dispatch

I am calling a dispatch function as below
this.props
.dispatch({
type: LOGIN_REQUESTED,
payload: {
username,
password,
},
})
i want to dispatch another action on successfully execution of this action
i tried with then function but getting error that this.props.dispatch(...).then is not a function
Since you're using redux saga, you can have a generator to watch for LOGIN_SUCCESS.
Dispatch this action after the API had successfully responded.
takeLatest(LOGIN_REQUESTED, watchForLogin)
takeLatest(LOGIN_SUCCESSFUL, watchForLoginSuccess)
watchForLogin(action) {
doLogin('/some/url')
.then(response => dispatch(loginSuccess(response))) // dispatch login success
.catch(error => dispatch(loginFailed(error))) // dispatch error
}
watchForLoginSuccess(action) {
doSomethingAfterLogin()
}

How to wait 'return' until 'dispatch' finish

[React JS]How to wait 'return' until 'dispatch' finish in action creator?
I don't know how handle this ;(
i made some code. this is a part of action creator. But it return before dispatch finish. i want to dispatch finish before 'return'. help me please
export const Hello = param=> dispatch => {
return postApi('/hello', param)
.then(async res => {
await dispatch(goHello(res));
return true;
})
By default, there really is no need to call return after dispatching an action. But if you'd like, you could use the getState() method to check that your action was processed before returning. getState() is the second argument of the redux-thunk function you are returning in your action-creator.
export const Hello = (param) => async (dispatch, getState) => {
postApi("/hello", param)
.then((res) => {
await dispatch(goHello(res))
})
.catch((err) => {
console.log(err.response.data)
})
//check if reducer was updated
const value = getState().keyOfReducerInStore <-- returns the state of your reducer, you can follow this up with whatever value you want to check
if(value){
return true
}
}
You can use await on postApi. You can only use await directly inside of an async function. Link.
export const Hello = (param) => async (dispatch, getState) => {
const res = await postApi("/hello", param); // <- code pauses here
const res2 = await anotherPostApi("/helloagain", param); // <- will wait for above
dispatch(goHello(res));
// You can also wrap the await postApi in a try catch instead of using .catch
const value = getState().keyOfReducerInStore <-- returns the state of your reducer, you can follow this up with whatever value you want to check
if(value){
return true
}
}
Just remember. Promises can be awaited as long as they are directly inside of an async function. If you console.log a variable it'll show you if it is a Promise.

Dispatch an action after other specific actions were dispatched and wait for state to change because of the first two actions

I'm trying to dispatch an action after other two actions were dispatched AND the state was changed because of those actions. I've read about redux-saga but I'm really looking forward to do it without it if possible (currently we're using redux-thunk).
I'm following a similar approach to the one Dan Abramov proposed in here: https://github.com/reduxjs/redux/issues/723#issuecomment-139927639.
The actions are being executed in the correct order, but the action inside the then() is executed before the state has changed due to firstActionDispatcher which the function that changes the state.
I'm using a promise.all using the next approach:
export const getData = categoryId => (dispatch, getState) => {
const state = getState();
const elements = state.categories && state.categories.elements && state.categories.elements[categoryId];
if (elements && elements.withMetadata) {
return false;
}
return Promise.all([
dispatch(firstActionDispatcher(categoryId)),
dispatch(secondActionDispatcher)
])
.then(() => dispatch({ type: THIRD_ACTION, data: Id }))
.catch(error => window.newrelic.noticeError(new Error(`[Error] ${error}`)));
};
Update Note 1: firstActionDispatcher is returning a promise and same for secondActionDispatcher,
I've been struggling with this for a couple of days now.
If you look at a snippet of code you've provided you are doing this:
return Promise.all([
dispatch(firstActionDispatcher(categoryId)),
dispatch(secondActionDispatcher)
])
.then(() => dispatch({ type: THIRD_ACTION, data: Id }))
Please note that secondActionDispatcher is not being invoked. I'm not sure if this is a typo, but you should likely be doing something like this:
return Promise.all([
dispatch(firstActionDispatcher(categoryId)),
dispatch(secondActionDispatcher()) // Notice the function call
])
.then(() => dispatch({ type: THIRD_ACTION, data: Id }))

Resources