NextJS: Waiting for the data from Google Firestore - javascript

So, I'm trying to get data from Google Firestore and I'm using NextJS framework.
Index.getInitialProps = async function () {
const db = await loadDB()
let data = []
db.firestore().collection('data').get().then(querySnapshot => {
querySnapshot.forEach(doc => {
data.push(doc.data())
console.log(data)
})
})
return {
data
}
}
So, on my "server" I get the data but in my Index component, it just remains an empty array... anyone know what I'm missing here to get the data to the component?
I'm assuming it's an await somewhere...

If you're wondering where the missing await is supposed to be, there's only one place. It only works with promises, and the get() returns a promise, since the query to Firestore is not immediately complete.
async function () {
const db = await loadDB()
let data = []
const querySnapshot = await db.firestore().collection('data').get()
querySnapshot.forEach(doc => {
data.push(doc.data())
console.log(data)
})
return {
data
}
})

Related

Store fetch data in variable to access it later

I'm facing a probably super easy to solve problem regarding fetching.
I'd like to fetch some json datas and store it in a variable to access it later.
The problem is that I always ends up getting undefined in my variable. What's the way to do to deal with that kind of data storing ?
Here's my code.
const fetchCities = () => {
fetch('cities.json')
.then(response => response.json())
.then(data => {
return data;
});
}
let cities = fetchCities();
console.log(cities)
Already looked up for answers but couldn't find a way to do. Thanks !
You could do this very simply with async/await like this:
const fetchCities = async () => {
let cities = await fetch('cities.json');
return cities.json();
};
let cities = await fetchCities();
console.log(cities);
Sending a fetch request takes time, so the console.log works before the data arrives.
The best way to deal with fetch is using async functions and await like so:
const fetchCities = ()=>{
return fetch('cities.json');
}
async function main(){
try {
const res = await fetchCities();
const data = await res.json();
// handle the data here, this will work only after the data arrival
console.log(data);
} catch (err) {
console.log(err);
}
}
main();
Note: await can only be used in async functions, that's the main purpose of the main function.
Or if you want to use .then:
const fetchCities = ()=>{
return fetch('cities.json');
}
function main(){
fetchCities()
.then(res => res.json())
.then(data => {
// handle the data here, all you code should be here
})
.catch (err => console.log(err));
}
main();

My async await method for fetching docs in Firestore using getDocs() returns an empty array

My async await method for fetching docs in Firestore using getDocs() returns an empty array.
I'm using React.js.
Thing is, this fetching-data-function is placed within a useEffect() hook with an empty array [] as a dependency (so that it runs, and hence fetches data from the Firestore database only once), and right after, the data is console logged. Output is an empty array.
allData: []
But if I just somehow get the useEffect() hook to run once more (like making a tiny change in the code and saving it - essentially just refreshing it on the local host), the array is populated with the desired data from the database.
This is the code:
import db from "./firebase";
useEffect(() => {
console.log("use effect ran");
const temp = async () => {
const q = query(collection(db, "blogs"));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
const newblog = {
id: doc.id,
title: doc.data().title,
content: doc.data().content,
};
setAllData((prev) => [...prev, newblog]);
});
};
temp();
console.log("allData: ", allData);
}, []);
I can't quite figure out what the issue is. I'd be grateful for some help.
You are seeing a empty array in allData because the console.log("allData: ", allData) is running before than setAllData((prev) => [...prev, newblog]) because the async function temp doesn't run in the same thread so the interpreter continues reading the code that follows. To fix it add await to temp() like this: await temp() and wrap it like I do in the following code.
import db from "./firebase";
useEffect(() => {
console.log("use effect ran");
(async () => {
const temp = async () => {
const q = query(collection(db, "blogs"));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
const newblog = {
id: doc.id,
title: doc.data().title,
content: doc.data().content
};
setAllData((prev) => [...prev, newblog]);
});
};
await temp();
console.log("allData: ", allData);
})();
}, []);

Passing external data into components

In my react app, I am currently passing a list of stores by calling the API directly from the URL.
const getStore = async () => {
try {
const response = axios.get(
'http://localhost:3001/appointment-setup/storeList'
);
return response;
} catch (err) {
console.error(err);
return false;
}
};
I pass this function into my useEffect hook where I would set my get a list of stores using resp.data.stores:
const [storeLocations, setStoreLocations] = useState([]);
useEffect(() => {
async function getData(data) {
await service.stepLocation.init();
const resp = await getStore();
setStoreLocations(resp.data.stores);
}
setFlagRender(true);
return getData();
}, []);
This works, however, I noted in useEffect there is a call await service.stepLocation.init(). There is a file that already takes care of all the backend/data for the component.
const stepLocation = {
// removed code
// method to retrieve store list
retrieveStoreList: async function ()
let response = await axios.get(
constants.baseUrl + '/appointment-setup/storeList'
);
return response.data.stores;
,
// removed code
Since this data is available, I don't need the getStore function. However when I try to replace response.data.stores in useEffect with service.stepLocation.retrieveStoreList no data is returned. How do I correctly pass the data from this file in my useEffect hook?
I think your useEffect should be like follows as you want to save the stores in your state.
useEffect(() => {
const updateStoreLocations = async () => {
const storeLocations = await service.stepLocation.retrieveStoreList();
setStoreLocations(storeLocations);
}
updateStoreLocations();
}, [])

React JS multiple API calls, data undefined or unexpected reserved word 'await' mapping through the data:

I'm creating a JS function that will make a call to an API, loop through the returned data and perform another call to retrieve more information about the initial data (for example where the first call return an ID, the second call would return the name/address/number the ID corresponds to). Positioning the async and await keywords though, have proven to be way more challenging than I imagined:
useEffect(() => {
const getAppointments = async () => {
try {
const { data } = await fetchContext.authAxios.get('/appointments/' + auth.authState.id);
const updatedData = await data.map(value => {
const { data } = fetchContext.authAxios.get('/customerID/' + value.customerID);
return {
...value, // de-structuring
customerID: data
}
}
);
setAppointments(updatedData);
} catch (err) {
console.log(err);
}
};
getAppointments();
}, [fetchContext]);
Everything get displayed besides the customerID, that results undefined. I tried to position and add the async/await keywords in different places, nothing works. What am I missing?
map returns an array, not a promise. You need to get an array of promises and then solve it (also, if your way worked, it would be inefficient waitting for a request to then start the next one.)
const promises = data.map(async (value) => {
const { data } = await fetchContext.authAxios.get('/customerID/' + value.customerID);
return {
...value,
customerID: data
};
});
const updatedData = await Promise.all(promises);

How can I call async function inside a loop?

I have below code in node. In getPosts, it reads 10 posts from database which is an async function call. And for each post, it needs to read user info. from database which is another async function call. How can I make it work in node js?
const getUser = async (userId) => {
// read user from database
}
const getPosts =async () => {
const posts = await getPostsFromDB(10); // get 10 posts from database
for(let i=0; i<posts.length; i++){
posts[i].user = await getUser(posts[i].userId) // ERROR: I can't call await inside a loop
}
}
I am thinking about using Promise.all() like below:
const getPosts =async () => {
const posts = await getPostsFromDB(10); // get 10 posts from database
const allProms = posts.map(post => getUser(post.userId));
Promise.all(allProms); // how can I assign each user to each post?
}
but I don't know how I can assign each user to each post after calling Promise.all().
Consider approaching the problem slightly differently. If you wait for responses in an iterative loop, it'll produce poor performance. Instead, you could push them all into an array and wait for them — so they're all fetching at the same time.
const getUser = async (userId) => {
try {
// read
} catch (e) {
// catch errors
}
// return data
}
const getPosts = async () => {
const posts = await getPostsFromDB(10); // get 10 posts from database
const userRequests = posts.map((post, index) => getUser(post.userId))
const users = await Promise.all(userRequests)
return posts.map((post, index) => {
post.user = users[index]
})
}
If you think you may have duplicate userIds, consider forming a list of users you can reference before calling getUser.

Categories