Promise.all result fires before actions succeed - javascript

I'm searching and searching solution on Stackoverflow and Github but 1.5 day is not enough - I don't see where I'm making mistake. I have following code of my component where I would like to fetch data from two endpoints and after store updates I console log something.
class ExampleComponent extends Component {
componentDidMount(){
this.fetchData()
}
fetchData(){
const {dispatch} = this.props
Promise.all([
dispatch(getTodos()),
dispatch(getPhotos())
])
.then(() => {
console.log('I did everything!');
});
}
render() {
return (
<h1>Something</h1>
);
}
}
export default connect()(ExampleComponent)
And here are my actions...
export function getPhotos() {
return function(dispatch) {
axios.get('https://jsonplaceholder.typicode.com/photos')
.then((response) => {
console.log('photos')
dispatch({type: 'PHOTOS_REQUEST_SUCCESS',payload: response})
})
.catch((err) => {
dispatch(photosRequestError(err))
})
}
}
export function getTodos() {
return function(dispatch){
axios.get('https://jsonplaceholder.typicode.com/todos')
.then(
(response) => {
console.log('todos')
dispatch({type: 'TODOS_REQUEST_SUCCESS', payload: response})
})
.catch((err) => {
dispatch(todosRequestError(err))
})
}
}
In console I get "I did everything", "todos", "photos". How should I modify my code to update store first and then log "I did everything" in console?
Thanks in advance

You need to return the promises themselves so that the chaining apply synchronous, so try adding return to axios.get('endpoint/url') in both functions.

Related

Vue store dispatch error response not being passed to UI

I'm trying to get the error response from my Vue store dispatch method, into my component, so I can tell the user if the save failed or not.
store/userDetails.js
const state = {
loading: {
user_details: false,
}
}
const getters = {
// Getters
}
const actions = {
save({commit, dispatch, rootState}, payload) {
commit('setLoading', {name: 'users', value: true});
axios(
_prepareRequest('post', api_endpoints.user.details, rootState.token, payload)
).then((response) => {
if (response.data) {
commit('setState', {name: 'user_details', value: response.data.units});
commit('setLoading', {name: 'user_details', value: false});
dispatch(
'CommonSettings/setSavingStatus',
{components: {userDetails: "done"}},
{root:true}
);
}
}).catch((error)=> {
console.log(error)
return error
}
)
}
My component method
views/Users.vue
send() {
this.$store.dispatch({
type: 'Users/save',
userDetails: this.current
}).then(response => {
console.log(response)
});
},
Above, I'm logging out the response in two places.
The response in my store/userDetails.js file is logged out fine, but it's not being passed to my send() function in my component - it comes up as undefined. Any reason why it wouldn't be passed through? Is this the correct way to do this?
This works for me. Try this solution.
store.js
actions: {
save(context, payload) {
console.log(payload);
return new Promise((resolve, reject) => {
axios(url)
.then((response) => {
resolve(response);
})
.catch((error) => {
reject(error);
});
});
},
},
My Component method
App.vue
save(){
this.$store.dispatch("save", dataSendToApi).then((response)=>{
console.log(response)
})
}
Try returning axios call in the Store Action:
// add return
return axios(
_prepareRequest('post', api_endpoints.user.details, rootState.token, payload)
)
.then() // your stuff here
.catch() // your stuff here
If that won't work, use Promise in the Store Action. Like this:
return new Promise((resolve, reject) => {
return axios() // simplify for readibility reason, do your stuff here
.then((response) => {
//... your stuff here
resolve(response) // add this line
})
.catch((error) => {
// ... your stuff here
reject(error) // add this line
})
})
you should return a promise, reference link:vue doc

async function not waiting for the await to end

I'm trying to add an async/await in my code to have the app wait for the execution of a function to invoke an other one, but I can't seem to be able to figure out where my code is wrong.
I have an API call in a redux action, this is the action
export const editSecondaryCategory = (text) => {
return (dispatch) => {
let obj = {
text
};
axios
.put(
`${ROOT_URL}/...`,
obj
)
.then(() => {
dispatch({ type: EDIT_NOTE, payload: [...annotations] });
dispatch(loadAllAnnotations(cid, uuid));
})
.catch((error) =>
dispatch(
notifSend({
message: error.message,
kind: "danger",
dismissAfter: 2000,
})
)
);
};
};
I want, in my component, to wait after this action is completed to call an other function to update the state. Ideally, it should be something like this (I guess?):
async updateCategory() {
// do the thing I have to wait for
const propsUpdate = await this.props.editSecondaryCategory(text);
// if you've done the thing then call the function to update the state
if (updatedCat) {
this.updateStateArray();
}
}
And I'd call this.updateCategory() inside my component after the user is done editing the information.
Clearly, this code does not work. I know it's wrong, I just don't know why. Basically I have no clue what to write inside updateCategory() to make this work.
Please help lol
You need to rewrite editSecondaryCategory function to make it async.
export async function editSecondaryCategory(text){
return (dispatch) => {
let obj = {
text
};
axios
.put(
`${ROOT_URL}/...`,
obj
)
.then(() => {
dispatch({ type: EDIT_NOTE, payload: [...annotations] });
dispatch(loadAllAnnotations(cid, uuid));
})
.catch((error) =>
dispatch(
notifSend({
message: error.message,
kind: "danger",
dismissAfter: 2000,
})
)
);
};
};
Currently, your function is not an async function do the above changes and check.

Loading screen with react to improve UX

I am little confused about loading data approach.
So i want to show loading screen on page load until all data is loaded, my approach is to make for each fetch method on this page a loading state. here I am using redux with thunk to dispatch async action.
actions-types:
export const IS_LOADING_SKILLS = "IS_LOADING_SKILLS";
export const SET_SKILLS = "SET_SKILLS";
export const IS_LOADING_INTRO = "IS_LOADING_INTRO";
export const SET_INTRO = "SET_INTRO";
actions:
import { SET_SKILLS, SET_INTRO,IS_LOADING_INTRO,IS_LOADING_SKILLS} from "./actions-type"
export const fetchSkills = () => {
return (dispatch) => {
dispatch(isLoadingSkills(true));
await api.get(url)
.then(({ data }) => dispatch(setSkills(data)));
dispatch(isLoadingSkills(false));
}
}
export const fetchIntro = () => {
return (dispatch) => {
dispatch(isLoadingIntro(true));
await api.get(url)
.then(({ data }) => dispatch(setIntro(data)));
dispatch(isLoadingIntro(false));
}
}
const setSkills = (payload) => {
return {
type: SET_SKILLS,
payload: payload
}
}
const setIntro = (payload) => {
return {
type: SET_INTRO,
payload: payload
}
}
const isLoadingSkills = (payload)=>{
return{
type:IS_LOADING_SKILLS,
payload:payload
}
}
const isLoadingIntro = (payload)=>{
return{
type:IS_LOADING_INTRO,
payload:payload
}
}
state:
const InitialState ={
loading:{
loadingIntro:false,
loadingSkills:false
},
data:{
intro:"",
skills:[],
}
}
now when both loading state for each fetch data method become false the loading screen will disappear.
I want to know if this is a good approach or what is better please explain. thank you!
This is a good approach, but your code has some weird parts. Either use async/await or .then/.catch callbacks (don't forget about the .catch, you could dispatch a setErrorMessage action or so).
So, with promises you would do:
export const fetchSkills = () => {
return (dispatch) => {
dispatch(isLoadingSkills(true));
api.get(
.then(({ data }) => {
dispatch(setSkills(data)));
dispatch(isLoadingSkills(false));
})
.catch((error) => ...stuff)
}
}
export const fetchIntro = () => {
return (dispatch) => {
dispatch(isLoadingIntro(true));
api.get(url)
.then(({ data }) => {
dispatch(setIntro(data)));
dispatch(isLoadingIntro(false));
})
.catch((error) => ...stuff)
}
}
And with async/await you would do:
export const fetchSkills = () => {
return async (dispatch) => {
try {
dispatch(isLoadingSkills(true));
const { data } = await api.get(url)
dispatch(setSkills(data)));
dispatch(isLoadingSkills(false));
} catch(error) {
...stuff
}
}
}
export const fetchIntro = () => {
return async (dispatch) => {
try {
dispatch(isLoadingIntro(true));
const { data } = await api.get(url)
dispatch(setIntro(data)));
dispatch(isLoadingIntro(false));
} catch(error) {
...stuff
}
}
}
Remember that async/await is just syntactic sugar for promises. Instead of using .then and .catch on a promise you just define the function using await as async (which allows you to use await AND returns a promise), and you will need to try/catch to catch any errors that can happen whilst doing the request.

Async action not running Async React Redux Thunk

I am trying to build a React App that fetches data from 7 api's, on initial load, then stores all the data in Redux, to use within the app. I am pretty new to React and a bit of a coding noob.
I have a dispatch function on my first app page that fires on componentDidMount() and runs the fetchData() function in the action below.
export function getDataPending(actionType) {
return {
type: "FETCH_" + actionType.toUpperCase() + "_PENDING"
};
}
export function getDataSuccess(actionType, data) {
return {
type: "FETCH_" + actionType.toUpperCase() + "_FULFILLED",
payload: data
};
}
export function getDataFailure(actionType, data) {
return {
type: "FETCH_" + actionType.toUpperCase() + "_REJECTED",
payload: data
};
}
//Sub data fetches
export function fetchNicSalts() {
return dispatch => {
dispatch(getDataPending("nic_salt"));
axios
.get(
"API_FEED"
)
.then(response => {
dispatch(getDataSuccess("nic_salt", response));
})
.catch(err => {
dispatch(getDataFailure("nic_salt", err));
});
};
}
export function fetchFreebase() {
return dispatch => {
dispatch(getDataPending("freebase"));
axios
.get(
"API_FEED"
)
.then(response => {
dispatch(getDataSuccess("freebase", response));
})
.catch(err => {
dispatch(getDataFailure("freebase", err));
});
};
}
//Main data fetch
export function fetchData() {
return dispatch => {
dispatch(getDataPending("data"));
dispatch(fetchFreebase());
dispatch(fetchNicSalts());
dispatch(getDataSuccess("data", null));
};
}
There are only two api fetches at the moment, as I am trying to get them to run async.
Ideally what I want to happen the dispatches to run in this order.
FETCH_DATA_PENDING
FETCH_FREEBASE_PENDING
FETCH_FREEBASE_FULFILLED
FETCH_NIC_SALT_PENDING
FETCH_NIC_SALT_FULFILLED
**** next api *****
FETCH_DATA_FULFILLED
The data is being stored at the moment just in the wrong order. Please see image for current order.
My data fulfillment order
As I say, I am self taught and not sure if I am on the right path, any advice is welcome. TIA.
You need to make your fetchFreebase and fetchNicSalts return the axios calls so that you can await them in the fetchData action.
This would look like this
export function getDataPending(actionType) {
return {
type: "FETCH_" + actionType.toUpperCase() + "_PENDING"
};
}
export function getDataSuccess(actionType, data) {
return {
type: "FETCH_" + actionType.toUpperCase() + "_FULFILLED",
payload: data
};
}
export function getDataFailure(actionType, data) {
return {
type: "FETCH_" + actionType.toUpperCase() + "_REJECTED",
payload: data
};
}
//Sub data fetches
export function fetchNicSalts() {
return dispatch => {
dispatch(getDataPending("nic_salt"));
return axios
.get(
"API_FEED"
)
.then(response => {
dispatch(getDataSuccess("nic_salt", response));
})
.catch(err => {
dispatch(getDataFailure("nic_salt", err));
});
};
}
export function fetchFreebase() {
return dispatch => {
dispatch(getDataPending("freebase"));
return axios
.get(
"API_FEED"
)
.then(response => {
dispatch(getDataSuccess("freebase", response));
})
.catch(err => {
dispatch(getDataFailure("freebase", err));
});
};
}
//Main data fetch
export function fetchData() {
return async (dispatch) => {
dispatch(getDataPending("data"));
await dispatch(fetchFreebase());
await dispatch(fetchNicSalts());
dispatch(getDataSuccess("data", null));
};
}

Im making my api call, but i am not able to return it back to my action creator

I have an action creator, it calls my apI(which is its own file), the api works, the action creator works as well. Im able to console.log my response from the api, my issue is returning it to my action creator, where the api function call was made. Thank You.
**ACTION CREATOR**
import * as types from './actionTypes';
import FootballApi from '../api/footballApi';
import { loadLeag_Success } from '.';
//type
export const LOAD_LEAG_GAMES_SUCCESS = 'LEAGUE_GAMES_SUCCESS';
export function loadLeag_Game_Success(data){
//console.log("before dis", data);
return;
};
export function getLeaguesGames(idArr, date) {
return function(dispatch) {
Promise.all(FootballApi.getLeaguesGamesAPI(idArr, date))
.then(() => {
// . I WANT TO RETURN IT HERE.
console.log("returned")
})
// .catch(error => {
// throw(error)
// })
};
}
**API FILE**
export default class FootballApi {
static getAllLeags() {
return fetch('https://apifootball.com/api/?APIkey=42f53c25607596901bc6726d6d83c3ebf7376068ff89181d25a1bba477149480&action=get_leagues').then(response => {
return response.json();
}).catch(error => {
return error;
});
}
static getLeaguesGamesAPI(idArr, date){
return idArr.map((id)=>{
return fetch(`https://apifootball.com/api/?APIkey=42f53c25607596901bc6726d6d83c3ebf7376068ff89181d25a1bba477149480&action=get_events&from=${date}&to=${date}&league_id=${id}`)
.then(res =>{
return res.json();
})
.then((game) => {
if(!game.error){
//console.log("game")
return game;
}
})
})
}
};
Assuming you already have the middleware(like thunk or saga) setup. Your Promise.All's resolve function doesn't take the data parameter.
Promise.all(FootballApi.getLeaguesGamesAPI(idArr, date))
.then((data) => {
dispatch(loadLeag_Game_Success(data));
});
Try doing this :
ACTION CREATOR
import * as types from './actionTypes';
import FootballApi from '../api/footballApi';
import { loadLeag_Success } from '.';
const LOAD_LEAG_GAMES_SUCCESS = 'LEAGUE_GAMES_SUCCESS';
export const loadLeag_Game_Success = (data) => {
console.log(data)// The data will be here
};
export function getLeaguesGames(idArr, date) {
return function(dispatch) {
Promise.all(FootballApi.getLeaguesGamesAPI(idArr, date))
.then((data) => {
dispatch(loadLeag_Game_Success(data));
})
.catch(error => {
throw(error)
})
}; }
I figured it out, i removed -
.then((game) => {
if(!game.error){
//console.log("game")
}
})
and put it in my action creator. everything works the way i need it. thanks.

Categories