I am trying to fetch data and set the state in React App. While the fetch is successful, as I can see the data in chrome dev tools, the execution stops at await statement in the below code. Only "getting data" is logged. Looks like after fetch statement the function returns, with all the following steps running successfully.
What am I doing wrong??
Any kind of help is much appreciated.
import util from "util";
const fetchProm = util.promisify(fetch)
....
getDataFromDb = async () => {
console.log('getting data')
let result = await fetchProm("http://localhost:3001/getData")
.then(data => {
console.log("then1:",data)
return data.json()
})
.then(res => {
console.log('then2:', res.data)
return { data: res.data }
})
.catch(err => {
return { err: err.data }
});
console.log("result:", result)
this.setState({ data: result.data })
};
you do not need .then callback if you use async-await.
try below sample :
import util from "util";
const fetchProm = util.promisify(fetch)
getDataFromDb = async () => {
console.log('getting data')
let {data} = await fetchProm("http://localhost:3001/getData");
console.log("result:", data)
this.setState({ data })
};
With async/wait you don't need the then. Also you catch errors on the second then and not the first.
Can you try:
let result = await fetchProm("http://localhost:3001/getData")
console.log(result)
and see if it works?
When using async/await, don't forget to handle your exceptions with try/catch
Change your code to:
import util from "util";
getDataFromDb = async () => {
try{
let {data} = await fetchProm("http://localhost:3001/getData");
this.setState({ data })
}
catch(err=> this.setState({ err: err.data }))
};
Related
I'm having a recurrent problem with react query. On some queries, the query says "fetching" (blue color), but the request to the server "doesn't go through". It's "suspended" or something. And I tried console.logging on my nodeJS controller (associated with the GET endpoint), but the function isn't even called.
This is quite annoying (and to be clear, this is with new queries which aren't stale or cached).
This is my query:
const postDetails = useQuery(
["post", postId],
async () =>
await fetch(
"http://localhost:5000/api/post/id/" + postId
).then((res) => res.json()),
{
onSuccess: (data) => {
console.log(data);
},
}
);
This is what React Query Devtools says:
This is what the network tab says:
Thanks in advance.
I believe your use of await and .then is giving you issues. I would recommend just sticking with await
const postDetails = useQuery(
["post", postId],
async () =>
try {
let res = await fetch(
"http://localhost:5000/api/post/id/" + postId
)
res = await res.json()
} catch(err) {
console.error(err)
}
{
onSuccess: (data) => {
console.log(data);
},
}
);
This method uses a trycatch for error handling, and does not rely on .then which I believe is where the issue lies.
Try separating the fetch function from the query:
async function fetchPosts() {
try {
const res = await fetch(
"http://localhost:5000/api/post/id/" + postId
)
const json = await res.json()
return json
} catch (err) {
console.error(err)
}
}
const postDetails = useQuery(
["post", postId],
fetchPosts,
{
onSuccess: (data) => {
console.log(data);
},
}
);
Here is [a simplified version] of my current code:
export const setTimestamp = async () => {
console.log("START")
let result = null;
const dbRef = await firestore.collection("collectionName").doc("docName");
const unsub = await dbRef.onSnapshot(snap => {
result = snap.data();
console.log("ACTUAL END", result);
});
await dbRef.set({ serverTime: firebase.firestore.FieldValue.serverTimestamp() });
unsub();
console.log("FUNCTION END", result);
return result;
};
FUNCTION END is currently logged before ACTUAL END whilst I need it the other way around. Any ideas? Are there perhaps alternatives to using onSnapshot?
Thank you for reading.
Well, onSnapshot is an event that returns a Promise for the subscription. But the event will be fired after subscription. If you want to make sure the event is fired you need another Promise that handles the scenario:
const res = await new Promise( async resolve=> {
const unsub = await dbRef.onSnapshot(snap => {
result = snap.data();
console.log("ACTUAL END", result);
resolve({result,unsub});
});
});
await dbRef.set({ serverTime: firebase.firestore.FieldValue.serverTimestamp() });
res.unsub();
console.log("FUNCTION END", res.result);
return res.result;
#Eldar's answer is correct but I subsequently found and prefer this approach:
import { v4 as UUID } from "uuid";
export const setTimestamp = async () => {
let result = null;
const dbRef = firestore.collection("collectionName").doc(UUID());
await bdRef.set({ serverTime: firebase.firestore.FieldValue.serverTimestamp() })
.catch(error => console.log("ERROR", error));
await dbRef.get()
.then(value => result = value.data())
.catch(error => console.log("ERROR", error));
return result;
};
My question is about correctly implementing an async function to fetch data. I've a function called _getData() and I'm calling it on the componentDidMount() of a screen. But when server response is slow, switching to this screen is getting slower. So I would like to use async function for fetching data. But I'm not sure if I'm doing it correctly. Is that a correct approach? I can't be sure if it works async or not.
Here is my Api._getData() code:
const _getData = async () => {
return await axios.get("http://blabla.com/someservice", { params: someParamDataHere });
};
export const Api = {
_getData
};
and on SomeScreen.js, I also have loadData() function which calls the function above and does state updates.
loadData() {
Api._getData()
.then((response) => {
this.setState({ myData: response.data });
})
.catch((error) => {
console.log(error.response);
});
}
in componentDidMount() function of the same screen I'm calling this loadData() function directly.
Now, is it enough to declare Api._getData() as async and using await in it, or should I change some trigger functions too?
Thank you very much for your help.
instead of async await use promises
export const getRequest = (url) => {
return new Promise((resolve, reject) => {
api
.get(url)
.then((response) => {
handleReponse(response)
.then((errorFreeResponse) => {
resolve(errorFreeResponse);
})
.catch((error) => {
reject(error);
});
})
.catch((error) => {
reject(handleError(error));
});
});
};
You are doing correct while retrieving in load Data . What you can do more is try more syntactical sugar of es6 by using async await in loadData , hence
loadData = async() =>{
try{
let response = await Api._getData();
this.setState({ myData: response.data });
} catch(err){
console.log(error.response);
}
}
Hope it helps. feel free for doubts
I've made a simple class that returns the data downloaded from firebase. The issue is that if I console.log data in the class, it gives data as expected. However, if I import this class anywhere else and try to use it, it returns data as undefined.
Can you explain what's wrong?
My getCollection func in class dbAPI (data is correct)
getCollection(collection) {
dataBase
.collection(collection)
.get()
.then(querySnapshot => {
let data = querySnapshot.docs.map(doc => doc.data())
console.log(data)
return data
})
.catch(function(error) {})
}
The way I try to get data (data is undefined here)
componentDidMount() {
const db = new dbAPI()
let data = db.getCollection("collectionName")
this.setState({ data })}
The issue is that you're trying to return data from a callback to a synchronous function, which is impossible (link). You need to either promisify your getCollection function, or use async/await. If you want to convert your code to use async/await, check out this link. I put an example below. Basically, you need to await for the get request to get the data, then perform your transformation. After performing that transformation, you can return data.
async getCollection(collection) {
const collectionRef = dataBase.collection(collection);
try {
const dataSnapshot = await collectionRef.get();
const data = querySnapshot.docs.map(doc => doc.data());
console.log(data);
return data;
} catch (err) {
console.log(err);
}
}
In your componentDidMount, you must add async/await keywords to the appropriate functions. Async needs to go to the top to mark componentDidMount as async, and you need to await the data from the function call db.getCollection.
async componentDidMount() {
const db = new dbAPI()
const data = await db.getCollection("collectionName")
this.setState({ data })
}
Thanks to you I've managed to do it, really appreaciate your help, my solution:
getCollection:
async getCollection(collection) {
let data = null
await dataBase
.collection(collection)
.get()
.then(querySnapshot => {
data = querySnapshot.docs.map(doc => doc.data())
})
.catch(function(error) {
console.error(`Data fetch failed: \n${error}`)
data = "ERROR"
})
return data}
getting data:
async componentDidMount() {
const db = new Database()
const data = await db.getCollection("collectionName")
this.setState({ ...data })}
I am trying to fetch data using AsyncStorage. whenever i call my action creator requestData and do console on the data which is passed , i get something like below .I have two version of getItem .In both the version i get useless value for property field . Property value should be readable
{"fromDate":"20160601","toDate":"20160701","property":{"_40":0,"_65":0,"_55":null,"_72":null},"url":"/abc/abc/xyz"}
async getItem(item) {
let response = await AsyncStorage.getItem(item);
let responseJson = await JSON.stringify(response);
return responseJson;
}
async getItem(item) {
try {
const value = AsyncStorage.getItem(item).then((value) => { console.log("inside componentWillMount method call and value is "+value);
this.setState({'assetIdList': value});
}).then(res => {
return res;
});
console.log("----------------------------value--------------------------------------"+value);
return value;
} catch (error) {
// Handle errors here
console.log("error is "+error);
}
}
componentWillMount() {
requestData({
fromDate: '20160601',
toDate: '20160701',
assetId: this.getItem(cmn.settings.property),
url: '/abc/abc/xyz'
});
}
You are getting property as a promise, you need to resolve it.
Try to use something link that.
assetId: this.getItem(cmn.settings.property).then((res) => res)
.catch((error) => null);
Since AsyncStorage is asynchronous in nature you'll have to wait for it to return the object AND THEN call your requestData method; something like the following -
class MyComponent extends React.Component {
componentWillMount() {
this.retrieveFromStorageAndRequestData();
}
async getItem(item) {
let response = await AsyncStorage.getItem(item);
// don't need await here since JSON.stringify is synchronous
let responseJson = JSON.stringify(response);
return responseJson;
}
async retrieveFromStorageAndRequestData = () => {
let assetId = await getItem(cmn.settings.property);
requestData({
fromDate: '20160601',
toDate: '20160701',
assetId,
url: '/abc/abc/xyz'
}) ;
}
// rest of the component
render() {
// render logic
}
}