can't get setState inside a firebase function out of it - javascript

first i set listaa as a global variable
i'm trying to take out the variable(array) "listaa" from the firebase function that sets it... I'm plannig to use it later to make some maths.
I used console.log(listaa) inside the firebase function and it worked,
but then when i try to use it() outside(the firebase function) it doens't work.
I tried to use setState to make it usable outside but didnt work too
if (funcionar == 0) {
//============================================================== ANTES ===================================================
var newRef = firebase
.database()
.ref()
.child("TransferenciasPraVendedores/")
.push({
vendedor: vend,
dia: functions.dataHoje(),
hora: functions.horaHoje()
});
var fire = database.ref("EstoqueCadaVendedor/" + vend);
fire.once("value", snap => {
items = [];
snap.forEach(data => {
items.push({
key: data.key,
data: data.val()
});
});
console.log("items:");
console.log(items);
for (j = 0; j < prod.length; j++) {
// var a = parseInt(snap.val().quantidade);
console.log("d: " + items[j].data);
listaa.push(items[j].data);
}
// this.setState({ lista:listaa })
// console.log("lista:");
// console.log(this.state.lista[2]);
console.log("listaa");
console.log(listaa);
});
console.log("listaaaaaaaaaaaaaa");
console.log(listaa);
Ill use 'listaa' here:
for (i = 0; i < prod.length; i++) {
firebase
.database()
.ref()
.child("EstoqueCadaVendedor/" + vend + "/")
.update({
[prod[i].key]: parseInt(quantNova[i]) + listaa[i]
});
}
// this.setState({ quantidade: [], vendedor: "" });
}

Tuan over here.
The reason why you don't see it is because your code is asynchronous, so when you print console.log() at the end. The firebase calls haven't finished yet.This is a typical race condition scenario.
FYI, I personally prefer to use the promise style rathen than the callback, I think it makes the code cleaner and easier to read. Try this:
let firebaseDB = firebase.database();
if (funcionar == 0) {
return firebaseDB.ref("TransferenciasPraVendedores/")
.push({
vendedor: vend,
dia: functions.dataHoje(),
hora: functions.horaHoje()
})
.then(() => {
return firebaseDB.ref("EstoqueCadaVendedor/" + vend).once('value');
.then(snapshot => {
let snap = snapshot.val();
items = [];
snap.forEach(data => {
items.push({
key: data.key,
data: data.val()
});
});
return {snap, items};
})
.then({snap, items} => {
for (j = 0; j < prod.length; j++) {
listaa.push(items[j].data);
}
//console.log(lista)// NOW your lista should have all the items
// I would dispatch your redux action here.
})
.catch(error => {
throw error
}
Let me know if it helped. I can look into it more in depth. Saludos :)

Did you review the order of the log messages ? if you look carefully, you will recognize the data gets available after later in the process. You probably need to use async/promise to wait until the data gets available or you can use a call back function
async
function to make sure the data is available in the list before you use it.
An async function can contain an await expression, that pauses the
execution of the function and waits for the passed Promise's
resolution, and then resumes the async function's execution and
returns the resolved value.
In the example below, I am passing a call back function that gets called when data is available :
export function listReleases(callback) {
//console.log("hello from list releases ");
releasesRef.once("value").then(
function(snapshot) {
const releases = snapshot.val();
if (releases) {
callback("releases", releases);
}
},
function(error) {
// The Promise was rejected.
console.error(error);
}
);
}
+++++++++++++++++++++++++++
//call the function to get the data and pass a call back function
releases = listReleases(this.myCallback);
myCallback = (key, value) => {
//do whatever you need to do in here
};

Related

Twilio Method inside of promise does not work

Reading
https://www.twilio.com/blog/implementing-programmable-chat-php-laravel-vue-js
I try implement chat in my Laravel 8 / jQuery 3.5.1 / vue 2.6 app.
This docs has defined :
setupChannel(channel){
let vm = this;
return this.leaveCurrentChannel()
.then(function() {
return vm.initChannel(channel);
})
.then(function(_channel) {
return vm.joinChannel(_channel);
})
.then(this.initChannelEvents);
},
I want to extend joinChannel method, as I want to make checks if current logged user (laravel auth)
is already joined. I try to make it with promise and failes, as code inside of
vm.tc.messagingClient.getSubscribedUsers() is not run. I do
setupChannel(channel){
let vm = this;
return this.leaveCurrentChannel()
.then(function() {
return vm.initChannel(channel);
})
.then(function(_channel) {
let promise = new Promise((resolve, reject) => {
// debugger
vm.tc.messagingClient.getSubscribedUsers().then(function(users) {
// THESE CODE IS NOT RUN. If to uncomment console and debugging it is not triggered
// console.log('++ users::')
// console.log(users)
// debugger
for (let i = 0; i < users.length; i++) {
const user = users[i];
console.log('user.identity: ' + JSON.stringify(user.identity) );
// console.log('user: ' + JSON.stringify(user, null, 2) );
if( user.identity === vm.loggedUser.name ) {
resolve("result")
}
}
debugger // THESE CODE IS NOT RUN
resolve("error")
})
})
console.log('++ promise::')
console.log(promise) // I SEE this promise in pending state
promise
.then(
result => {
alert("result: " + result);
return _channel;
},
error => {
alert("error: " + error);
return vm.joinChannel(_channel);
}
)
// return vm.joinChannel(_channel);
})
.then(this.initChannelEvents);
If to run code
vm.tc.messagingClient.getSubscribedUsers().then(function(users)
...
inside of promise, it works ok and I got valid results.
What is wrong in my promise structure and how can I fix it?
MODIFIED BLOCK:
I try to follow your way with :
joinGeneralChannel() {
console.log('Attempting to join "general" chat channel...');
let vm = this;
if (this.tc.generalChannel == null) {
console.log('SETTING this.tc.messagingClient.createChannel')
vm.loadChannelList(vm.joinGeneralChannel);
}else {
// console.log('Found general channel:');
this.setupChannel(this.tc.generalChannel);
}
},
async setupChannel(channel) {
let vm = this
await this.leaveCurrentChannel()
const newChannel = await vm.initChannel(channel)
const subscribedUsers = vm.tc.messagingClient.getSubscribedUsers()
console.log('subscribedUsers::')
console.log(subscribedUsers)
let isUserJoined = false
for (let i = 0; i < subscribedUsers.length; i++) {
console.log('subscribedUsers[i] ' + JSON.stringify(subscribedUsers[i]) );
if( subscribedUsers[i].name === vm.loggedUser.name ) {
isUserJoined = true``
break
}
}
debugger
console.log('isUserJoined::')
console.log(isUserJoined)
But in the cosole of my browser I see :
Initialized channel General Channel
TeamChat.vue?e1c8:561 subscribedUsers::
TeamChat.vue?e1c8:562 PromiseĀ {<pending>}__proto__: Promise[[PromiseState]]: "pending"[[PromiseResult]]: undefined
TeamChat.vue?e1c8:573 isUserJoined::
looks like method getSubscribedUsers is asynchronous ?
Thanks!
Probably your Promise fails, that's why then() will never execute. To extend joinChannel method you can do something like this with async/await and ES6 syntax:
async setupChannel(channel) {
let vm = this;
try {
await this.leaveCurrentChannel();
const newChannel = await vm.initChannel(channel);
const users = await vm.tc.messagingClient.getSubscribedUsers();
const isUserJoined = users.some(({ name }) => name === vm.loggedUser.name);
const joinedChannel = isUserJoined ? newChannel : vm.joinChannel(_channel);
return this.initChannelEvents(joinedChannel);
} catch(err) {
// if some of promises below will fail, here you'll see details
console.log('Issue details here:', err);
}
}

firebase cloud functions oncall returns null

wondering what's the weird error.
I am using the onCall method from firebase cloud functions, but when I read it from my app it returns null value. I am trying to return some test data but it doesn't seem to be working. Am i returning the data wrongly?
index.js
exports.handleMassFollowAnalytics = functions.https.onCall((data, context) => {
const brandArray = data.brandArray;
const followed = data.followed;
let done = 0;
for (var i = 0; i < brandArray.length; i++) {
let brand = brandArray[i];
admin.database()
.ref(`brands/${brand}/followers`)
.transaction(function(post) {
if (post !== null) {
post--;
}
return post;
},
function(error, committed, snapshot) {
done++;
if (done === brandArray.length) {
// returning result.
return {
data: "testabc",
};
}
}
);
}
});
app.js
const handleMassFollowAnalytics = firebase
.functions()
.httpsCallable("handleMassFollowAnalytics");
handleMassFollowAnalytics({
brandArray: array,
followed: true,
}).then((result) => {
console.log("result: ", result) // returns null everytime
});
Your function needs to return a promise that resolves with the data to send to the client. Right now, your function returns nothing. The return statement inside the transaction callback is not returning from the main function.
Also, the code is ignoring the promises returned by the transactions you're performing. The final promise returned from the function must resolves only after all the other promises resolve.
So, I used Doug's information and arrived at the following answer, for reference to anyone in future.
This seems to return correctly for me.
Return individual promises
Return final promise
index.js
exports.handleMassFollowAnalytics = functions.https.onCall((data, context) => {
const brandArray = data.brandArray;
const followed = data.followed;
var promises = [];
for (var i = 0; i < brandArray.length; i++) {
let brand = brandArray[i];
promises.push(admin.database()
.ref(`brands/${brand}/followers`)
.transaction(function(post) {
if (post !== null) {
post--;
}
return post;
});
);
}
return Promise.all(promisess).then((result)=>{
return {
data: "testabc",
}
})
});

How to edit value at the end of for loop in javascript

I run the function below to fetch product Infos, I save every request on array, very basic API stuff
fetch = id => {
var url = "https://myapi.server.com";
var request = new Request(url + "/api/v1/fetch/", {
method: "POST"
});
fetch(request)
.then(response => {
response.json().then(results => {
this.setState({
items: this.state.items.concat([results.data])
});
});
})
.catch(function(err) {
console.log(err.message);
});
};
to init this function it runs inside for loop like so
var favs = JSON.parse(value);
for (let x in favs) {
this.fetch(favs[x].id);
}
if I add setState loading: false in the function it will show data on screen and mess things up, I wish to wait until the array is done I have tried the approach below but it kinda funky, it doesn't work, the logic seems right to me
var length = favs.length - 1;
for (let x in favs) {
this.fetch(favs[x].id);
if (x === length) {
this.setState({ loading: false });
}
}
If anyone can assist, please any suggestion appreciated
You can organize your data into array of promises and use Promise all, so state will be set only after all data is loaded:
Promise.all(favs.map((item) => this.fetch(item.id))).then(() => {
this.setState({loading: false})
})
If you just want to recognize last iteration, this is one of the ways:
const favs = [1,2,3,4,5]; // or var favs = JSON.parse(value)
let counter = 0;
for(let x in favs) {
counter++;
console.log( counter==favs.length ? "last one" : "not last" );
console.log(x);
/*
if (counter==favs.length) {
this.setState({loading: false})
}
*/
}
In your example, you compared x (which is probably object) to a number.
It doesn't work beacuse this.fetch is an asyncronous function. So you basically start n calls and you don't wait for them to finish before set the loading to false;
I'll suggest a little refactoring.
The Promise.all function might be your answer.
fetch = (id) => {
var url = 'https://myapi.server.com'
var request = new Request(url + '/api/v1/fetch/', {
method: 'POST',
})
return fetch(request)
.catch(function (err) {
console.log(err.message)
})
}
.
.
.
var favs = JSON.parse(value)
var fetchCalls = favs.map(fav => this.fetch(fav.id));
Promise.all(fetchCalls).then((results) => {
this.setState({
items: [...results.data] // or items: [...items,...result.data]
});
this.setState({loading: false});
});
i am not sure but i think you are looking for something like this
https://www.techup.co.in/react-native-pagination-example-infinity-list/
Promise.all will not be optimal solution if data size is large
Not 100% sure about the === operator here - so why not simply call it after the loop?
for (let x in favs) {
this.fetch(favs[x].id)
}
this.setState({loading: false})
it's easier to read and should work.
Edit: as fetch() is asynchronous, you need a different approach:
// somewhere outside the function
var fetchCount = favs.count;
// in your function
for (let x in favs) {
this.fetch(favs[x].id).then(decreaseFetchCount());
}
// define this somewhere outside
function decreaseFetchCount ()
{
fetchCount -= 1;
if(fetchCount == 0)
this.setState({loading: false})
}

Accessing method vars from inside arrow function

In my vuetify/vue.js appliction, i'm trying to read a firestore collection I've stored to calculate an average rating from all ratings stored there:
function updateRating() {
let firestoreQuery = firebase
.firestore()
.collection('ratings')
.where('qrCodeId', '==', this.qrCode.id);
let ratingsSum = 0;
let ratingsAmountCounter = 0;
firestoreQuery.get().then(querySnapshot => {
querySnapshot.forEach(doc => {
ratingsSum += doc.rating;
ratingsAmountCounter++;
});
});
}
However, I can't access the ratingsSum and ratingsAmountCounter variables from inside my forEach arrow function. I've already tried using this or the var _this = this workaround, but both don't seem to work and eslint still reports:
'ratingsSum' is assigned a value but never used (no-unused-vars)
What am I doing wrong?
As the comments suggest, the problem here is that you have no code that actually reads ratingsSum and ratingsAmountCounter. The error message is telling you that ESLint has triggered on the rule called "no-unused-vars" to tell you about this. Variables that are never read are probably a bug, or at least unnecessary code.
What you should probably do is return a promise to the caller so that they can get a hold of the values, whenever the asynchronous get() is complete and the values are computed. For example:
function updateRating() {
let firestoreQuery = firebase
.firestore()
.collection('ratings')
.where('qrCodeId', '==', this.qrCode.id);
let ratingsSum = 0;
let ratingsAmountCounter = 0;
return firestoreQuery.get().then(querySnapshot => {
querySnapshot.forEach(doc => {
ratingsSum += doc.rating;
ratingsAmountCounter++;
});
return { ratingsSum, ratingsAmountCounter }
});
}
The caller of this function now receives a promise that resolves when the values are known, and they can be fetched out of the resolved object:
updateRating().then(result => {
const { ratingsSum, ratingsAmountCounter } = result;
// Now you can use ratingsSum and ratingsAmountCounter...
})
this might work, I have no way to test it since I don't know how to use vue but here goes
async function getData(){
return await firebase
.firestore()
.collection('ratings')
.where('qrCodeId', '==', this.qrCode.id)
.get()
}
})
you can then do this:
getData().then(_querySnapshot => {
var ratingsSum = 0
var ratingsAmountCounter = 0
_querySnapshot.forEach(doc => {
ratingsSum += doc.rating;
ratingsAmountCounter++;
});
});

Firebase Function returning Undefined values, even with promises

I'm new on Firebase, I've a simple program that fetches product information using "ProductID".
Here's the hierarchy:
PRODUCT CHILD
The problem is, when I use this function, it returns undefined values. I know it's asynchronous, and I've tried adding promises, but still in vain. Here's it:
function getProduct(prID){
var db = admin.database();
var ref = db.ref("server/products/" + prID);
let rData = '';
ref.on("value", function(snapshot) {
rData = snapshot.val();
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
return rData;
Calling the same function with same values returns the product, but calling for different parameter values, it returns undefined.
I've tried using (in a function getProductPromise(prID) ), I changed above function to:
function getProductPromise(id) {
var db = admin.database();
var ref = db.ref("server/products/" + id);
let rData = '';
return ref.once('value').then(function(snapshot) {
return snapshot.val();
});
}
it is still returning undefined values. What could be the possible way to ensure that some values are returned.
Thanks.
Looks like you're having some scoping issues in the first example, so not sure if it would ever work as you're expecting. Promises are your best bet. You can try something like:
function fetchProductById (id) {
var db = admin.database()
var collectionRef = db.ref('server/products')
var ref = collectionRef.child(id)
return ref.once('value')
.then((snapshot) => {
return snapshot.val()
})
}
// then to implement:
fetchProductById('abc')
.then((result) => {
console.log('the result is', result)
})

Categories

Resources