Vue - call async action only after first one has finished - vuejs2

I need to call 2 actions from inside my component, but the second should only start after the first one has 100% finished it's job.
I'm trying this but it doesn't work
mounted() {
this.$store.dispatch('coinModule/loadApiCoins')
.then(() => {
this.$store.dispatch('coinModule/loadUserCoins')
})
.catch(error => {
console.log(error)
});
},
and the 2 actions are these
loadApiCoins({ commit, dispatch, rootGetters }) {
Vue.axios({
method: 'get',
url: 'https://api.coinmarketcap.com/v1/ticker/',
transformRequest: [(data, headers) => {
delete headers.common.Authorization
return data
}]
})
.then(response => { commit('SET_API_COINS', response.data) })
.catch(error => { console.log(error) })
},
loadUserCoins({ commit, dispatch, rootGetters }) {
Vue.axios.get('http://127.0.0.1:8000/api/coins/')
.then(response => {
commit('SET_USER_COINS', response.data)
commit('SET_USER_PORTFOLIO_OVERVIEW')
})
.catch(error => { console.log(error) })
}
These should be the other way around.
Screen of my network tab

When you dispatch an action, it doesn't have a then callback by default. That's only the case if the action returns a Promise. Your axios.get call should return a Promise, but you aren't returning it in your action. You should simply return it and then then callback will fire in your mounted hook.
loadApiCoins({ commit, dispatch, rootGetters }) {
return Vue.axios({
method: 'get',
url: 'https://api.coinmarketcap.com/v1/ticker/',
transformRequest: [(data, headers) => {
delete headers.common.Authorization
return data
}]
})
.then(response => { commit('SET_API_COINS', response.data) })
.catch(error => { console.log(error) })
},

Related

How make Axios "finally" waiting until "then" is finished?

I'm trying to the finally be executed after the method getCharacters is finished, but without success.
He always is executed before.
There is a way to make him be executed after everything in the then is finished?
I suspect that the problem is the axios inside the getCharacters method.
new Vue({
el: '#app',
data () {
return {
message: '',
}
},
methods: {
getCharacters() {
axios
.get('/api/messages/characters')
.then(response => {
console.log('finish get')
})
},
submitForm: function (event) {
axios
.post('/api/messages/send', {
message: this.message
})
.then(response => {
this.getCharacters()
})
.finally(() => {
console.log('finish post')
})
}
}
})
You have to return the result of getCharacters:
.then(response => {
return this.getCharacters()
})
or
.then(response =>
this.getCharacters()
)

What is the function of this Vue "dispatch"?

I have this existing working VueJs code
const actions = {
retrieveStatus({ rootState, commit, dispatch }) {
return Axios
.get('/abc/GetStatus', {
params: {
draftId: rootState.eform.Id
}
})
.then(response => {
commit('SET_STATUS', response.data.statusCode);
return response.data;
})
.catch(err => {
throw new Error('Errors');
})
},
I don't see anywhere it uses dispatch but it exists there.

How to invoke component method from store.js

I have Login.vue component which has method postData()
postData() {
this.$store.dispatch('doLogin', fdata)
},
doLogin is in store.js
actions: {
doLogin({ commit }, loginData) {
commit('loginStart');
axios.post(this.state.apiURL+'/login', {
...loginData
})
.then(response => {
commit('loginStop', null);
commit('updateAccessToken', response.data.access_token);
})
.catch(error => {
commit('loginStop', error);
})
},
how to invoke back from router.js a method in other component, let say again from Login.vue?
I want in general invoke Toast which is bootstrap thing in Login.vue methods.
Any idea?
Login.vue component method
this.$store.dispatch('doLogin', fdata).then(response => {
console.log("response from promise:",response)
}, error => {
this.makeToast(true, error, 'danger', 'b-toaster-top-center')
console.error("Got error:",error)
})
store.js Actions
doLogin({ commit }, loginData) {
return new Promise((resolve, reject) => {
console.log("store - doLogin", loginData)
commit('loginStart');
axios.post(this.state.apiURL+'/login', {
...loginData
})
.then(response => {
localStorage.setItem('accessToken', response.data.access_token);
commit('loginStop', null);
commit('updateAccessToken', response.data.access_token);
router.push('/profile');
resolve(response);
})
.catch(error => {
console.log('error', error)
commit('loginStop', error);
commit('updateAccessToken', null);
reject(error)
})
})
},

Asynchronous VuexFire Data Fetch With Then/Catch

Given the following functioning vuex action named init that fetches a settings and an accounts collection:
actions: {
init: firestoreAction(({ bindFirestoreRef, commit }, payload) => {
bindFirestoreRef(
'settings', fb.settings.doc(payload),
)
.then(() => commit('SETTINGS_READY', true))
.catch((err) => {
commit('SNACKBAR_TEXT', err.message);
Sentry.captureException(err);
});
bindFirestoreRef(
'accounts', fb.accounts.where('program', '==', payload),
)
.then(() => commit('ACCOUNTS_READY', true))
.catch((err) => {
commit('SNACKBAR_TEXT', err.message);
Sentry.captureException(err);
});
}),
},
I have two questions:
The code appears to run synchronously, but I want the two collections to be fetched asynchronously to maximize performance. How can that be achieved?
Is it possible to refactor this code to be more concise and yet provide for the independent (and synchronous) then/catch functionality present in the example?
You can use async / await function then call the bindFirestoreRef inside Promise constructor.
actions: {
init: firestoreAction(async ({ bindFirestoreRef, commit }, payload) => {
await Promise((resolve, reject) => {
bindFirestoreRef('settings', fb.settings.doc(payload))
.then((res) => {
commit('SETTINGS_READY', true);
resolve(res);
})
.catch((err) => {
commit('SNACKBAR_TEXT', err.message)
Sentry.captureException(err)
reject(err)
})
})
await new Promise((resolve, reject) => {
bindFirestoreRef('accounts', fb.accounts.where('program', '==', payload))
.then((res) => {
commit('ACCOUNTS_READY', true);
resolve(res);
})
.catch((err) => {
commit('SNACKBAR_TEXT', err.message)
Sentry.captureException(err)
reject(err)
})
})
})
},

Only navigate to next page when asynchronos actions are complete? React-native

So, I have a bit of a tricky situation here for me as a beginner with redux as well as react-native.
When the user loggs in, I want to update the Redux state with the user data. I call a login methond where I get a web token. Directly afterwards I want to dispatch two asynchronous actions with redux-thunk. The problem is:
By the time these actions are dispatched and I have the response from the API, I've already navigated to another screen and the data to render the list is not in the Redux state.
The Question: How can I "hold" the program until my state is updated and then navigate to the next page?
This is what happens when the user logs in:
fetch("http://10.0.2.2:8000/api/api-token-auth/", {
method: "post",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: this.props.email,
password: this.props.password,
})
}).then((response) => response.json()
).then((jResponse) => {
console.log(jResponse);
this._onValueChange('token_id', jResponse.token);
this.props.loginUserSuccess();
this.props.navigation.navigate('MainMenue');
}).catch((error) => {
console.log(error);
this.props.loginUserFail();
})
}
Somewhere during the login these two actions sould be dispatched completly and the state should be updated:
export const profileLoad = () => {
return (dispatch) => {
AsyncStorage.getItem('token_id')
.then((token_id) => fetch("http://10.0.2.2:8000/api/profile/", {
method: "GET",
headers: {
'Authorization': 'JWT ' + token_id
}
})
.then((response) => response.json())
.then((answer) => {
dispatch({ type: PROFILE_LOAD, payload: answer});
})
.done());
}
}
export const productsLoad = () => {
return (dispatch) => {
AsyncStorage.getItem('token_id')
.then((token_id) => {
fetch("http://10.0.2.2:8000/api/profile/products/", {
method: "GET",
headers: {
'Authorization': 'JWT ' + token_id
}
}).then((anser) => anser.json())
.then((response)=> {
dispatch ({ type: PRODUCTS_LOAD, payload: response})
})
}
).done();
}
}
Then I want to navigate the another screen andrender a list (with ListView) to display the JSON data from products and profiles.
-- > So I finally figured it out.
Solution
1.) Return promises from action creators as stated
2.) Make sure you put a callback function in the then method
export const loadAllProfileData = ({navigate}) => {
return (dispatch) => {
dispatch(profileLoad())
.then(() => dispatch(productsLoad()))
.then(() => navigate('MainMenue'))
};
}
export const profileLoad = () => {
return (dispatch) => {
return AsyncStorage.getItem('token_id')
.then((token_id) => fetch("http://10.0.2.2:8000/api/profile/", {
method: "GET",
headers: {
'Authorization': 'JWT ' + token_id
}
})
).then((response) => response.json())
.then((answer) => {
dispatch({ type: PROFILE_LOAD, payload: answer});
})
}
}
export const productsLoad = () => {
return (dispatch) => {
return AsyncStorage.getItem('token_id')
.then((token_id) =>
fetch("http://10.0.2.2:8000/api/profile/products/", {
method: "GET",
headers: {
'Authorization': 'JWT ' + token_id
}
})
).then((answer) => answer.json())
.then((response)=> {
dispatch ({ type: PRODUCTS_LOAD, payload: response})
})
}
}
You can return promises from your action creators and chain them with then. You can do that by simply adding return AsyncStorage.getItem() ... to your action creators. Then you can do:
fetch(url) //login
.then(dispatch(profileLoad))
.then(dispatch(productsLoad))
.then(this.props.navigation.navigate('MainMenue'))
.catch(err => //handle error)
Read more about promises chaining.
Edit: A simple example would be:
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import fetch from 'node-fetch';
const ROOT_URL = 'https://jsonplaceholder.typicode.com';
const FETCH_DATA = 'FETCH_DATA';
const url = `${ROOT_URL}/users`;
function fetchData() {
return (dispatch) => {
return fetch(url)
.then(res => res.json())
.then(data => {
dispatch({
type: FETCH_DATA,
payload: data[0].name
});
})
}
}
function reducer(state = [], action) {
if (action.type === FETCH_DATA) {
console.log('Action.payload:', action.payload);
}
switch (action.type) {
case 'FETCH_DATA':
return [...state, action.payload];
default:
return state;
};
}
let store = createStore(
reducer,
applyMiddleware(thunkMiddleware)
)
store.subscribe(() =>
console.log('Store State: ', store.getState())
)
fetch(url)
.then(res => res.json())
.then(data => data)
.then(store.dispatch(fetchData()))
.then(store.dispatch(fetchData()))