Async and Await not working in Axios React - javascript

i have a problem:
I want that my axios make the requistion and after it makes the this.setState with the result saved in a variable.
My code:
componentDidMount() {
let mails = [];
axios.get('/api/employee/fulano')
.then(res => this.setState({
employees: res.data
}, () => {
this.state.employees.map(i => {
async axios.get(`/api/status/${i.mail}`)
.then(res => {
mails.push(res.data)
await this.setState({
mails: mails
})
})
.catch(err => console.log(err))
})
}))
.catch(err => console.log(err))
}
But it gives error syntax.
Best explanation: I want saved all results of the map in the variable mails and later to use the setState to changes the result of just a time.
Someone could tell me where i'm wandering? Please.

You are using async await at the wrong places. async keyword must be used for a function that contains asynchronous function
await keyword needs to be used for an expression that returns a Promise, and although setState is async, it doesn't return a Promise and hence await won't work with it
Your solution will look like
componentDidMount() {
let mails = [];
axios.get('/api/employee/fulano')
.then(res => this.setState({
employees: res.data
}, async () => {
const mails = await Promise.all(this.state.employees.map(async (i) => { // map function contains async code
try {
const res = await axios.get(`/api/status/${i.mail}`)
return res.data;
} catch(err) {
console.log(err)
}
})
this.setState({ mails })
}))
.catch(err => console.log(err))
}

It's not a good practice to mix async/await with .then/.catch. Instead use one or the other. Here's an example of how you could do it using ONLY async/await and ONLY one this.setState() (reference to Promise.each function):
componentDidMount = async () => {
try {
const { data: employees } = await axios.get('/api/employee/fulano'); // get employees data from API and set res.data to "employees" (es6 destructing + alias)
const mails = []; // initialize variable mails as an empty array
await Promise.each(employees, async ({ mail }) => { // Promise.each is an asynchronous Promise loop function offered by a third party package called "bluebird"
try {
const { data } = await axios.get(`/api/status/${mail}`) // fetch mail status data
mails.push(data); // push found data into mails array, then loop back until all mail has been iterated over
} catch (err) { console.error(err); }
})
// optional: add a check to see if mails are present and not empty, otherwise throw an error.
this.setState({ employees, mails }); // set employees and mails to state
} catch (err) { console.error(err); }
}

This should work:
componentDidMount() {
axios.get('/api/employee/fulano')
.then(res => this.setState({
employees: res.data
}, () => {
this.state.employees.map(i => {
axios.get(`/api/status/${i.mail}`)
.then( async (res) => { // Fix occurred here
let mails = [].concat(res.data)
await this.setState({
mails: mails
})
})
.catch(err => console.log(err))
})
}))
.catch(err => console.log(err))
}

You put async in the wrong place
async should be placed in a function definition, not a function call
componentDidMount() {
let mails = [];
axios.get('/api/employee/fulano')
.then(res => this.setState({
employees: res.data
}, () => {
this.state.employees.map(i => {
axios.get(`/api/status/${i.mail}`)
.then(async (res) => {
mails.push(res.data)
await this.setState({
mails: mails
})
})
.catch(err => console.log(err))
})
}))
.catch(err => console.log(err))
}

Related

Is there a better way to handle api calls in javascript/react

I've build a function to make an api call to a weather service and then update state based on if it resolves, or rejects. The code works perfectly, but it feels messy to me.
getForecastedWeather(){
const endpoint = `http://dataservice.accuweather.com/forecasts/v1/daily/5day/207931?apikey=mG0ISFW1ZGGHIV3rs5CSFQlF92CYSqhr&language=en&details=true&metric=true`;
const fetchPromise = fetch(endpoint);
fetchPromise
.then(response => response.json())
.then(result => {
this.setState(state => ({
forecasts: {
...state.forecasts,
error: null,
isLoaded:true,
forecasts:result.DailyForecasts
},
}));
}).catch(error => {
this.setState(state => ({
forecasts: {
...state.forecasts,
error,
isLoaded:false
},
}));
})
}
Is there a better way of handling this?
Please consider using async await syntax:
async getForecastedWeather(){
try {
const endpoint = `http://...`;
const response = await fetch(endpoint);
const result = await response.json();
this.setState(state => ({ ... }));
} catch (error) {
this.setState(state => ({ ... }));
}
}

Proper error handling and response in react with express and Mongoose

// delete function
delete =(index) => {
const st = this.state.data;
const newSt = st[index]._id;
// fetch delete api
fetch(`http://localhost:4000/users/${newSt}`, {
method: 'DELETE'
}, (err,result) => {
if(err){
console.log(err)
}else{
result.json({'msg': 'ok'})
}
})
st.splice(index,1)
this.setState({data: st})
}
I just created a delete function for my react-express-mongoose app. but the (err,result) isnt working. What did I do wrong? (the delete function works) I am just confused about the {(err,result) => {...} and what should I do inside it.
I believe fetch is a promise so it needs the following syntax
fetch(opts)
.then(result => {
console.log(result);
})
.catch(err => {
console.error(err);
});
Fetch API: Using Fetch
Please make your function like the following structure.
// delete function
delete =(index) => {
const st = this.state.data;
const newSt = st[index]._id;
// fetch delete api
fetch(`http://localhost:4000/users/${newSt}`, {method: 'DELETE'}).then((res) => { console.log(res); })
, (err,result) => {
if(err){
console.log(err)
}else{
result.json({'msg': 'ok'})
}
})
st.splice(index,1)
this.setState({data: st})
}

Use callback or promise to make async code to work like sync in nodejs

My nodejs api function:
exports.userSignup = (req, res) => {
const home = {
address: req.body.name,
phoneno: req.body.code,
};
Home.create(home)
.then((data) => {
createUser()
// i want to complete the above createUser() function fully then only i need to move to this below then function
.then(() => {
const loginDetails = {
username: 'stackoverflow',
};
User.create(loginDetails)
.then((data) => {
return res.status(200).send(data);
}).catch((err) => {
console.log('error while create schema:', err);
});
});
})
.catch((err) => {
console.log('err:', err);
});
};
My createUser function code:
const createUser = () => {
Home.findAll({
raw: true,
}).then((data) => {
data.forEach((client) => {
postgresDB.createSchema(client.code).then(() => {
Object.keys(postgresDB.models).forEach((currentItem) => {
postgresDB.models[currentItem].schema(client.code).sync();
});
console.log('Postgres schema created');
}).catch((err) => {
console.log(err);
});
});
}).catch((err) => {
console.log('Warning:', err.message);
});
};
createUser();
But this one is working as asynchronous,
How to make this using promise resolve reject or callback?
See my code, i made a comment which needs to work first,
I tried with async await but not working!
In order for promise chaining to work, you have to return a promise for any asynchronous functions or else it won't wait. You're also making another asynchronous call for each client you iterate through. In order to deal with multiple promises at once, you need to push each promise into an array and pass it to Promise.all. Here's a snippet that should work.
const createUser = () => {
return Home.findAll({
raw: true,
}).then((data) => {
const promises = [];
data.forEach((client) => {
promises.push(
postgresDB.createSchema(client.code).then(() => {
Object.keys(postgresDB.models).forEach((currentItem) => {
postgresDB.models[currentItem].schema(client.code).sync();
console.log('Postgres schema created');
})
})
);
});
return Promise.all(promises);
}).catch((err) => {
console.log('Warning:', err.message);
});
};
The problem lies in the synchronous data.forEach() call inside the createUser function which doesn't wait for the async createSchema calls to finish before returning.
You can fix this by using an async implementation of forEach function or by using Promise.all()
Here's a piece of code that might work for you:
const createUser = () => {
return Home.findAll({
raw: true
}).then((data) => Promise.all(
data.map((client) => postgresDB.createSchema(client.code).then(() =>
Promise.all(Object.keys(postgresDB.models).map((currentItem) =>
postgresDB.models[currentItem].schema(client.code).sync()
))
))
))
.catch((err) => {
console.log('Warning:', err.message);
});
};
If async/await doesn't quite work for you, just use promise chaining. It's super painful to see people just write callback based code with promises and forget that you can chain promises by returning a promise from the onFulfilled() parameter to then():
const createUser = () => {
return Home.findAll({ raw: true }).
then(data => {
return Promise.all(data.map(client => postgresDB.createSchema(client.code)));
}).
then(() => {
const keys = Object.keys(postgresDBModels);
return Promise.all(keys.map(currentItem => {
return postgresDB.models[currentItem].schema(client.code).sync();
}));
}).
then(() => console.log('Postgres schema created')).
catch((err) => {
console.log(err);
});
};
createUser();
You can use async and await in createUser function. You can alter your function by Using promise, We can able to either resolve or reject.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

Javascript fetch api

i'm trying to assign the variable countries to the response of the fetch api but when i print out countries i get undefined ? can anyone explain to me why ? and a solution if it is possible.
async function getCountries(url) {
const data = await fetch(url);
const response = await data.json();
return response;
}
let countries;
document.addEventListener('DOMContentLoaded', () => {
getCountries('https://restcountries.eu/rest/v2/all')
.then(res => countries = res)
.catch(err => console.log(err));
})
console.log(countries);
function getCountries(url) {
return new Promise((resolve,reject)=>{
fetch(url).then(res => {
//You can parse the countries from the api response here and return to the
//event listener function from here using the resolve methood call parameter
resolve(res);
})
.catch(err => console.log(err));
})
}
document.addEventListener('DOMContentLoaded', () => {
getCountries('https://restcountries.eu/rest/v2/all')
.then(res => {
//the returned value after api call and parsing will be available here in res
})
})
Or if you dont want another function for this one you can directly get it using the following way,
document.addEventListener('DOMContentLoaded', () => {
let countries;
fetch('https://restcountries.eu/rest/v2/all')
.then(res => {
//the returned value after api call and parsing out the countries hereit self
countries = res.data.countries;
})
})

Why won't my fetch response by passed in my dispatch?

I'm trying to use a node server as the intermediary between firebase and my react native app.
Can someone please tell me what I'm doing wrong here with my fetch?
export const fetchPostsByNewest = () => {
return (dispatch) => {
fetch('http://localhost:3090/')
.then((response) => {
console.log(response.json());
//dispatch({ type: NEW_POSTS_FETCH_SUCCESS, payload: response.json()});
});
};
};
this is the node/express router:
const router = (app) => {
app.get('/', (req, res) => {
firebase.database().ref('/social/posts')
.once('value', snapshot => res.json(snapshot.val()));
});
};
If I console.log the response.json() then I just get this:
If I console.log response, I get this:
How can I get rid of the headers? If I do console.log(response._bodyInit) then I get this:
Which looks like what I need.
But if I pass it through as payload then I get this error:
My previous action creator directly worked with firebase, like this:
export const fetchPostsByNewest = () => {
return (dispatch) => {
firebase.database().ref('/social/posts')
.once('value', snapshot => {
console.log(snapshot.val())
dispatch({ type: NEW_POSTS_FETCH_SUCCESS, payload: snapshot.val() });
});
};
};
If I console.log(snapshot.val()). Then I got this:
This works just fine and looks the same as the last console.log of console.log(response._bodyInit).
What am I doing wrong here? I'd really appreciate any help.
Thank you!
response.json() returns a promise, so you need another link in the Promise chain
export const fetchPostsByNewest = () => {
return (dispatch) => {
fetch('http://localhost:3090/')
.then((response) => response.json())
.then((json) => {
console.log(json);
dispatch({ type: NEW_POSTS_FETCH_SUCCESS, payload: json});
});
};
};
If you don't need to console.log the json
export const fetchPostsByNewest = () => {
return (dispatch) => {
fetch('http://localhost:3090/')
.then((response) => response.json())
.then((json) => dispatch({ type: NEW_POSTS_FETCH_SUCCESS, payload: json}));
};
};
Fetch works a little different, when you call response.json() that returns another promise, so you need to chain another then after you return the new promise:
fetch('http://localhost:3090/')
.then(response => response.json())
.then(aFunctionToDoSomethingWithTheJson);

Categories

Resources