Hi I have exported using data (hawkers collection) using getDocs() from Firebase.
After that I put each hawker data as an object in an array called allStall as shown in the screenshot of the console log below.
Question 1 - How do I access each individual object in my allStall array. I try to use .map() to access each of it, but i am getting nothing.
Do note that I already have data inside my allStall array, see screenshot above.
[Update] map doesn't work in code below because field is stallname not stallName. However, it needs to be async + await if using/call in/from other function.
Question 2 - Why is there [[Prototype]]: Array(0) in my allStall array
export /*Soln add async*/function getAllStall(){
var allStall = [];
try
{
/*Soln add await */getDocs(collection(db, "hawkers")).then((querySnapshot) =>
{
querySnapshot.forEach((doc) =>
{
var stall = doc.data();
var name = stall.stallname;
var category = stall.category;
var description = stall.description;
var stallData = {
stallName:name,
stallCategory:category,
stallDescription:description
};
allStall.push(stallData);
});});
console.log(allStall);
//Unable to access individual object in Array of objects
allStall.map(stall =>{console.log(stall.stallName);});}
catch (e) {console.error("Error get all document: ", e);}
return allStall;
}
In my main js file, i did the following:
useEffect(/*Soln add await*/() =>
{
getAllStall();
/*Soln:replace the statement above with the code below
const allStall = await getAllStall();
allStall.map((stall)=>console.log(stall.stallname));
*/
}
);
You are getting nothing because allStall is empty since you are not waiting for the promise to be fullfilled
try this
export const getAllStall = () => getDocs(collection(db, "hawkers"))
.then((querySnapshot) =>
querySnapshot.map((doc) =>
{
const {stallName, category, description} = doc.data();
return {
stallName:name,
stallCategory:category,
stallDescription:description
};
});
)
try to change use effect like this
useEffect(async () =>
{
const allStats = await getAllStall();
console.log(allStats)
allStats.forEach(console.log)
}
);
A very big thanks to R4ncid, you have been an inspiration!
And thank you all who commented below!
I managed to get it done with async and await. Latest update, I figure out what's wrong with my previous code too. I commented the solution in my question, which is adding the async to the function and await to getDocs.
Also map doesn't work in code above because field is stallname not stallName. However, it needs to be async + await if using in/calling from other function.
Helper function
export async function getAllStall(){
const querySnapshot = await getDocs(collection(db, "hawkers"));
var allStall = [];
querySnapshot.forEach(doc =>
{
var stall = doc.data();
var name = stall.stallname;
var category = stall.category;
var description = stall.description;
var stallData = {
stallName:name,
stallCategory:category,
stallDescription:description
};
allStall.push(stall);
}
);
return allStall;
}
Main JS file
useEffect(async () =>
{
const allStall = await getAllStall();
allStall.map((stall)=>console.log(stall.stallname));
}
);
Hurray
Related
I am trying to make barbershop web app where costumer can see list of free appointments and when they reserve free appointment I want to delete that field from firebase.
I have a collection which represents one barber.
This is how it looks in firebase.
As you see radno_vrijeme is object or map in firebase which contains 6 arrays, and in each array there is list of free working hours.
In my function I am able to do everthing except last line where I need to update firebase collection.
const finishReservation = async () => {
try {
const freeTimeRef = collection(db, `${barber}`);
const q = query(freeTimeRef);
const querySnap = await getDoc(q);
querySnap.forEach(async (doc) => {
const radnoVrijeme = doc.data().radno_vrijeme;
// Find the index of the hour you want to delete
const index = radnoVrijeme["Mon"].indexOf(hour);
// Remove the hour from the array
radnoVrijeme["Mon"].splice(index, 1);
// Update the document in the collection
console.log(radnoVrijeme);
const radnoVrijemeMap = new Map(Object.entries(radnoVrijeme));
await freeTimeRef.update({ radno_vrijeme: radnoVrijemeMap });
});
} catch (error) {
console.log(error);
}
};
I tried to pass it as JSON stringified object, but it didn't work. I always get this error :
"FirebaseError: Expected type 'ya', but it was: a custom Ia object"
When you are trying to fetch multiple documents using a collection reference or query, then you must use getDocs():
const finishReservation = async () => {
try {
const freeTimeRef = collection(db, `${barber}`);
const q = query(freeTimeRef);
const querySnap = await getDocs(q);
const updates = [];
querySnap.forEach((d) => {
const radnoVrijeme = d.data().radno_vrijeme;
const index = radnoVrijeme["Mon"].indexOf(hour);
radnoVrijeme["Mon"].splice(index, 1);
const radnoVrijemeMap = new Map(Object.entries(radnoVrijeme));
updates.push(updateDoc(d.ref, { radno_vrijeme: radnoVrijemeMap }))
});
await Promise.all(updates);
console.log("Documents updated")
} catch (error) {
console.log(error);
}
};
getDoc() is used to fetch a single document using a document reference.
Good Afternoon,
I am using the MERN stack to making a simple invoice application.
I have a function that runs 2 forEach() that goes through the invoices in the DB and the Users. if the emails match then it gives the invoices for that user.
When I log DBElement to the console it works, it has the proper data, but when I log test1 to the console (app.get()) it only has one object not both.
// forEach() function
function matchUserAndInvoice(dbInvoices, dbUsers) {
dbInvoices.forEach((DBElement) => {
dbUsers.forEach((userElement) => {
if(DBElement.customer_email === userElement.email){
const arrayNew = [DBElement];
arrayNew.push(DBElement);
app.set('test', arrayNew);
}
})
})
}
// end point that triggers the function and uses the data.
app.get('/test', async (req,res) => {
const invoices = app.get('Invoices');
const users = await fetchUsersFromDB().catch((e) => {console.log(e)});
matchUserAndInvoice(invoices,users,res);
const test1 = await app.get('test');
console.log(test1);
res.json(test1);
})
function matchUserAndInvoice(dbInvoices, dbUsers) {
let newArray = [];
dbInvoices.forEach((DBElement) => {
dbUsers.forEach(async(userElement) => {
if(DBElement.customer_email === userElement.email){
newArray.push(DBElement);
app.set('test', newArray);
}
})
})
}
app.set('test', DBElement); overrides the existing DBElement, so only the last matching DBElement is shown in test1.
If you want to have test correspond to all matching DBElement, you should set it to an array, and then append a new DBElement to the array each time it matches inside the for-loop:
if(DBElement.customer_email === userElement.email){
let newArray = await app.get('test');
newArray.push(DBElement);
app.set('test', newArray);
}
I saw similar questions online but none of their solutions worked for me.
I am building an app in React Native which loads information from firebase and then displays it. I want to load objects from firebase, put them in an array and then set the state so the class would re-render and display it once it's loaded.
The information is being loaded fine, but I can't find a way to call setState after the array has loaded. I tried promises and tried using another function as a callback, but nothing had worked for me yet. It always executes setState before the array is loaded. I don't know if using setTimeout in some way would be a good solution though.
Here is the some of the code (I want to update the jArray in this.state and then re-render the page) :
constructor(props){
super(props);
this.state = {
jArray: []
}
}
componentDidMount(){
this.getJ();
}
async getJ(){
let jArray = [];
let ref = database.ref('users/' + fb.auth().currentUser.uid + '/usersJ');
let snapshot = await ref.once('value');
let itemProcessed = 0;
let hhh = await snapshot.forEach(ch => {
database.ref('J/' + ch.val()).once('value')
.then(function(snapshot1){
jArray.push(snapshot1);
itemProcessed++;
console.log(itemProcessed);
if(snapshot.numChildren()===jArray.length){
JadArray = jArray
}
})
});
}
Thanks (:
Maybe you can do something like this:
// don't forget to use an arrow function to bind `this` to the component
getJ = async () => {
try {
const ref = database.ref('users/' + fb.auth().currentUser.uid + '/usersJ');
const snapshot = await ref.once('value');
// it might be easier just to start by getting the data into an object you can use like this
const dataObj = snapshot.val();
// extract the keys
const childKeys = Object.keys(dataObj);
// use the keys to create a function that makes an array of all the promises we want
const createPromises = () =>
childKeys.map(childKey => database.ref('J/' + childKey).once('value'));
// await ALL the promises before moving on
const jArray = await Promise.all(createPromises());
// now you can set state
this.setState({ jArray });
// remember to catch any errors
} catch (err) {
console.warn(err);
// you might want to do something else to handle this error...
}
};
}
So in the end I found a solution to my problem, from: https://stackoverflow.com/a/47130806/3235603
My code looks like this:
async getJ(){
let jArray = [];
let ref = database.ref('users/' + fb.auth().currentUser.uid + '/usersJ');
let snapshot = await ref.once('value');
let itemProcessed = 0;
let that = this;
let hhh = await snapshot.forEach(ch => {
database.ref('J/' + ch.val()).once('value')
.then(function(snapshot1){
jArray.push(snapshot1);
itemProcessed++;
console.log(itemProcessed);
if(snapshot.numChildren()===jArray.length){
JadArray = jArray
that.setState({
jadArray : jArray,
dataLoaded : true
},() => console.log(that.state))
}
})
});
}
It's kinda a tricky one with 'this' and 'that', but it all works fine now.
I am getting JSON from a api GET call. I am trying to get a value of a item which I think is a array. I am trying to console log the low price of the json.
I tried to cycle through it like a array like open.openDate.btcusd[5] and so on.
//JSON DATA FROM API
btcusd":{
"high":"9206.36",
"low":"8804.57",
"volume":"1291.122483",
"last":"8989.64",
"bid":"8987.88",
"ask":"8998.24"
//Call
coin.getOpen()
.then(data=>{
coin.ui(data);
});
//Function
async getOpen(){
const openres = await
fetch(`https://api.lakebtc.com/api_v2/ticker`);
const openBtc = await openres.json();
return {
openDate : openBtc
}
}
//New Function to console.log
ui(open){
console.log(open.openDate.btcusd); //I want the low value
}
do call this function like
async function getOpen(){
const openres = await fetch(`https://api.lakebtc.com/api_v2/ticker`);
const openBtc = await openres.json();
// console.log(openBtc.btcusd.low);
return {
openDate : openBtc
}
}
// function call
getOpen()
.then(res => console.log(res.openDate.btcusd.low))
.catch(err => console.error(err))
Didnt completely get your requirement here, but the response here is an object not an array.
We can convert to array and print the low values for different entries like :
async function getOpen(){
const openres = await fetch(`https://api.lakebtc.com/api_v2/ticker`);
const openBtc = await openres.json();
return {
openDate : openBtc
}
};
getOpen().then(data=>{
ui(data);
});
function ui(obj){
var arr = Object.entries(obj.openDate);
var lowValues = arr.map(d => console.log(d[0] + " value of low is " + d[1].low));
}
I'm trying to improve a firestore get function, I have something like:
return admin.firestore().collection("submissions").get().then(
async (x) => {
var toRet: any = [];
for (var i = 0; i < 10; i++) {
try {
var hasMedia = x.docs[i].data()['mediaRef'];
if (hasMedia != null) {
var docData = (await x.docs[i].data()) as MediaSubmission;
let submission: MediaSubmission = new MediaSubmission();
submission.author = x.docs[i].data()['author'];
submission.description = x.docs[i].data()['description'];
var mediaRef = await admin.firestore().doc(docData.mediaRef).get();
submission.media = mediaRef.data() as MediaData;
toRet.push(submission);
}
}
catch (e) {
console.log("ERROR GETTIGN MEDIA: " + e);
}
}
return res.status(200).send(toRet);
});
The first get is fine but the performance is worst on the line:
var mediaRef = await admin.firestore().doc(docData.mediaRef).get();
I think this is because the call is not batched.
Would it be possible to do a batch get on an array of mediaRefs to improve performance?
Essentially I have a collection of documents which have foreign references stored by a string pointing to the path in a separate collection and getting those references has been proven to be slow.
What about this? I did some refactoring to use more await/async code, hopefully my comments are helpful.
The main idea is to use Promise.all and await all the mediaRefs retrieval
async function test(req, res) {
// get all docs
const { docs } = await admin
.firestore()
.collection('submissions')
.get();
// get data property only of docs with mediaRef
const datas = await Promise.all(
docs.map(doc => doc.data()).filter(data => data.mediaRef),
);
// get all media in one batch - this is the important change
const mediaRefs = await Promise.all(
datas.map(({ mediaRef }) =>
admin
.firestore()
.doc(mediaRef)
.get(),
),
);
// create return object
const toRet = datas.map((data: MediaSubmission, i) => {
const submission = new MediaSubmission();
submission.author = data.author;
submission.description = data.description;
submission.media = mediaRefs[i].data() as MediaData;
return submission;
});
return res.status(200).send(toRet);
}