I create reactjs app when user ask for login I send three request
getToken();
createLogWithToken();
createRequestwithLog();
every function depend on the pervious one
here is my code
getToken = async (){
axios.post('getToken')
.then(response => {
this.setState({ token: response.data.token });
})
}
createLogWithToken = async (){
axios.post('createLogWithToken',{token:this.state.token})
.then(response => {
this.setState({ log_id: response.data.log_id });
})
}
createRequestwithLog = async (){
axios.post('createRequestwithLog',{token:this.state.token, log_id: this.state.log_id})
.then(response => {
this.setState({ request_id: response.data.request_id });
})
}
onPress = async () =>{
await this.getToken();
await this.createLogWithToken();
await this.createRequestwithLog();
}
I got error on the second commands as the required parameters is null
How can I call then and await till the the setstate done
Edit >>
I changed my code to be
getToken = async (){
await axios.post('getToken')
.then(response => {
this.setState({ token: response.data.token });
return (response.data.token);
})
}
createLogWithToken = async (token){
await axios.post('createLogWithToken',{token})
.then(response => {
this.setState({ log_id: response.data.log_id });
return response.data.log_id;
})
}
createRequestwithLog = async (token, log_id){
await axios.post('createRequestwithLog',{token, log_id})
.then(response => {
this.setState({ request_id: response.data.request_id });
return response.data.request_id;
})
}
onPress = async () =>{
let token = await this.getToken();
let log = await this.createLogWithToken(token);
let request = await this.createRequestwithLog(token,log);
}
but I still get error that token undefine
Several things:
The functions you are awaiting aren't "awaitable". You need to return a promise.
You can just await your axios calls directly like this const token = await axios.post...
setState isn't synchronous. You don't know if it's defined by the time you call the next endpoint. You should just set them in normal variables. If you also need them as state, you can do that too, but it doesn't look necessary
See How to structure nested Promises
fetch(url)
.then(function(response) {
return response.json()
})
.then(function(data) {
// do stuff with `data`, call second `fetch`
return fetch(data.anotherUrl)
})
.then(function(response) {
return response.json();
})
.then(function(data) {
// do stuff with `data`
})
.catch(function(error) {
console.log('Requestfailed', error)
});
getToken = async () {
const token = (await axios.post('getToken')).data.token;
this.setState({ token });
return token;
}
createLogWithToken = async (token) {
const logId = (await axios.post('createLogWithToken',{ token })).data.log_id;
this.setState({ log_id: logId });
return logId;
}
createRequestwithLog = async (token, logId) {
const requestId = (await axios.post('createRequestwithLog',{ token, log_id: logId })).data.request_id;
this.setState({ request_id: requestId});
}
onPress = async () => {
const token = await this.getToken();
const logId = await this.createLogWithToken(token);
this.createRequestwithLog(token, logId);
}
Related
So i have two functions that fetch data from the API. One of them updates the todo completion, and the other one fetches the list of todos. Currently the UpdateAndFetch() function lags behind, and sometimes returns the not updated list.
How can i fix this?
Functions that make API calls
let base = import.meta.env.VITE_API_BASE
// fetch the todos that belong to groupId
export async function TESTFetchTodosFromGroup(groupId, todos, groupName) {
let url = `${base}/todos/group/${groupId}`
fetch(url).then(async (response) => {
const json = await response.json()
todos.value = json.data
groupName.value = json.message
if (!response.ok) {
return Promise.reject(error)
}
})
}
//
export async function TESTupdateCompletion(todo) {
//
let url = `${base}/todos/completion/${todo.todoId}`
var requestOptions = {
method: 'PATCH',
redirect: 'follow',
}
fetch(url, requestOptions)
.then((response) => response.json())
.then((result) => console.log(result))
.catch((error) => console.log('error', error))
}
Function inside the Component
async function UpdateAndFetch(todo, groupId) {
const updateTodoCompletion = await TESTupdateCompletion(todo)
const updateTodoList = await TESTFetchTodosFromGroup(groupId, todos, groupName)
}
You always have to return a fetch function otherwise it will not pass the value to the next async call.
And in order to have the ability to execute the function one by one you can do this where you call your functions.
async function UpdateAndFetch(todo, groupId) {
Promise.resolve(() => updateTodoCompletiong(todo)).then(response => fetchTodosFromGroup(groupId,todos,groupName))
}
after that, you can catch errors.
You can read the documentation here it is really very helpful what javascript provides. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve
also if any questions leave a comment.
Nevermind, found how to fix this.
Wrap the fetch functions to be a return value
// fetch the todos that belong to groupId
export async function fetchTodosFromGroup(groupId, todos, groupName) {
let url = `${base}/todos/group/${groupId}`
// you need to wrap the fetch into return so that the awaits would work !
return fetch(url).then(async (response) => {
const json = await response.json()
todos.value = json.data
groupName.value = json.message
if (!response.ok) {
return Promise.reject(error)
}
})
// Promise.resolve('done')
}
//
export async function updateTodoCompletion(todo) {
//
let url = `${base}/todos/completion/${todo.todoId}`
var requestOptions = {
method: 'PATCH',
redirect: 'follow',
}
// you need to wrap the fetch into return so that the awaits would work !
return (
fetch(url, requestOptions)
.then((response) => response.json())
.then((result) => console.log(result))
// .then(() => TESTFetchTodosFromGroup())
.catch((error) => console.log('error', error))
)
}
Refactor the function that executes the functions
// delete the todo and fetch the list
async function UpdateAndFetch(todo, groupId) {
await updateTodoCompletion(todo)
await fetchTodosFromGroup(groupId, todos, groupName)
}
I'm working on a React project where I have a function that fetches paginated data recursively. This function is defined in another function, where I activate the loading screen with showLoading() at the start, where I'm calling the fetching data function in the body and deactivate the loading screen with hideLoading() at the end. The function:
const fetchPlaylist = (playlistId) => {
showLoading()
const getPlaylistDataRecursively = (url) => {
fetch('/spotify/get-track-ids', {headers: {
'url': url
}})
.then(response => response.json())
.then(data => {
console.log(data)
setTitles(data.title)
setArtists(data.artist)
setFeatures(data.features)
setIds(data.track_ids)
if (data.next_url) {
const next_url = data.next_url.replace('https://api.spotify.com/v1', '')
getPlaylistDataRecursively(next_url)
}
})
}
getPlaylistDataRecursively(`/playlists/${playlistId}/tracks/?offset=0&limit=100`)
hideLoading()
}
I believe I can attach a promise to the getPlaylistDataRecursively call with the then keyword but I'm getting a TypeError: Cannot read property 'then' of undefined. Probably because getPlaylistDataRecursively doesn't return anything. How do I make sure that hideLoading is called after getPlaylistDataRecursively is done?
You always need to return a promise.
const fetchPlaylist = (playlistId) => {
showLoading()
const getPlaylistDataRecursively = (url) => {
return fetch('/spotify/get-track-ids', {headers: {
'url': url
}})
.then(response => response.json())
.then(data => {
console.log(data)
setTitles(data.title)
setArtists(data.artist)
setFeatures(data.features)
setIds(data.track_ids)
if (data.next_url) {
const next_url = data.next_url.replace('https://api.spotify.com/v1', '')
return getPlaylistDataRecursively(next_url)
}
})
}
return getPlaylistDataRecursively(`/playlists/${playlistId}/tracks/?offset=0&limit=100`)
.then(() => hideLoading());
}
Or, using async/await:
const fetchPlaylist = async (playlistId) => {
showLoading()
const getPlaylistDataRecursively = async (url) => {
const response = await fetch('/spotify/get-track-ids', {headers: {
'url': url
}})
const data = response.json()
console.log(data)
setTitles(data.title)
setArtists(data.artist)
setFeatures(data.features)
setIds(data.track_ids)
if (data.next_url) {
const next_url = data.next_url.replace('https://api.spotify.com/v1', '')
await getPlaylistDataRecursively(next_url)
}
}
await getPlaylistDataRecursively(`/playlists/${playlistId}/tracks/?offset=0&limit=100`)
hideLoading()
}
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))
}
I got unexpected identifier but not sure what is the mistake. I'm using fetch which is already a promise.
async getUsers = () => {
const resp = await fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
return resp
}
getUsers().then(users => console.log(users))
Notice the position of the async keyword:
Not:
async getUsers = () => {
But:
getUsers = async () => {
Run:
getUsers = async () => {
const resp = await fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
return resp;
};
getUsers().then(users => console.log(users))
As per comments:
Should I chain then() in getUsers()? async/await suppose to eliminate then() am I right?
Yes, you can await any Promise. Or use both .then() sometimes and await at others (like does the code above). But you could just use async/await as well.
The example below uses no .then():
getUsers = async () => {
const resp = await fetch('https://jsonplaceholder.typicode.com/posts/1')
return resp.json();
};
(async () => {
// notice to use the await keyword, the code must be wrapped in an async function
const users = await getUsers();
console.log(users);
})();
Aside from the typo in the async word pointed by #acdcjunior, you're mixing async / await with the usual promise handling (.then()) which is not wrong but kind of defeats the point. Using only async / await would look like:
const getUsers = async () => {
const resp = await fetch('https://jsonplaceholder.typicode.com/posts/1');
return resp.json();
}
async function fetchUsers() {
try {
const users = await getUsers();
console.log(users);
} catch(err) {
console.log(err);
}
}
fetchUsers();
you have the syntax wrong:
const getusers = async () => {
...
}
const is optional
Your syntax of declaring your function is wrong, here's some explanation.
If getUsers is a method of a react component class the syntax should be :
getUsers = async () => {
const resp = await fetch(
'https://jsonplaceholder.typicode.com/posts/1'
).then(response => response.json());
return resp;
};
or :
async getUsers() {
const resp = await fetch(
'https://jsonplaceholder.typicode.com/posts/1'
).then(response => response.json());
return resp;
};
If it's outside of a react component class or in a stateless arrow function component you can use this syntax :
const getUsers = async () => {
const resp = await fetch(
'https://jsonplaceholder.typicode.com/posts/1'
).then(response => response.json());
return resp;
};
const getUsers = async () => {
try {
const resp = await fetch('https://jsonplaceholder.typicode.com/posts/1');
return resp.json();
} catch(e) {
console.error(e)
}
}
(async () => {
const users = await getUsers();
console.log(users)
})()
Use this, and run
I have the following snippet of code
export const fetchPosts = () => async dispatch => {
const res = await axios.get(`${url}/posts`, { headers: { ...headers } });
console.log(res.data);
let posts = res.data.map(p => (p.comments = fetchComments(p.id)));
console.log(posts);
dispatch({ type: FETCH_POSTS, payload: res.data });
};
export const fetchComments = id => async dispatch => {
console.log(id)
const res = await axios.get(`${url}/posts/${id}/comments'`, {
headers: { ...headers }
});
console.log("id", id);
return res.data;
};
when i console log the posts, i get 2 functions returned. what is the proper way in which i should call the fetch comments for this function to return me the desired value?
Add this:
const postsResult = await Promise.all(posts)