Hey Guys I'm using Firebase Realtime Database to fetch some data for my React.js Web App.
There is a useState called Corr_User that should store the value of correct username.
I have a function to fetch the username from Firebase : -
function Login(){
const path = firebase.database().ref("users").child(username);
path.once("value")
.then(snapShot => {
setCorr_User(snapShot.child("username").val());
})
When I do this console.log(Corr_User) it prints a empty string indicating that the useState is not updated.
For confirmation, I also logged the snapshot.val(). This printed the appropriate value.
I could understand from this that Firebase is too slow in returning response hence my useState is not getting updated. Is my understanding correct? Is there any way to make my Login() function wait for the response?
Please Help !! Thanks in Advance.
EDIT -
Ok there was a bit of confusion I guess. The code goes likes this
function Login(){
....
.once("value")
.then(snapShot => { // After fetching the data
setCorr_User(snapShot.child("username").val()); // Assigning the value
console.log(snapShot.val()); // This should be logged first
}) // End of function snapShot()
// This is still inside Login()
console.log(Corr_User) // But this is logged first
} // End of Login()
It is from this, I was able to guess that Firebase is too slow in responding and useState is not getting updated accordingly as snapShot.val() is still empty.
Data is loaded from Firebase asynchronously, and the then is called when that data is available. What this means in practice is that your console.log(Corr_User) is called before console.log(snapShot.val()). For this reason, any code that needs data from the database must be inside the then callback, or be called from there.
This is an incredibly common stumbling block for developers new to asynchronous APIs, so I recommend studying some of these links:
Why Does Firebase Lose Reference outside the once() Function?
Best way to retrieve Firebase data and return it, or an alternative way
Firebase Query inside function returns null
Passing variable in parent scope to callback function
Related
Im trying to assign variables to their respected value from the firestore database using the get doc function, I've noticed it does not assign or update the values what so ever.
I've tried to work with async and awaits but cannot seem to make it work.
getFromDatabase(nameOfCollection,nameOfDocument){
const db = firebase.firestore();
var docRef = db.collection(nameOfCollection).doc(nameOfDocument);
docRef.get().then(function(doc) {
if (doc.exists) {
outvariable = doc.data().anyfield; // THIS IS WHAT I WANT
console.log(" Document data:", doc.data());
} else {
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
}
im expecting outvariable = doc.data().anyfield
Most likely you're confused by the fact that data is loaded from Firestore asynchronously. It's not so much that the data isn't assigned to the values, because it really is. It just happens at a different time than you expect.
It's easiest to see this by adding some simple logging statements around the code that loads data:
const db = firebase.firestore();
var docRef = db.collection(nameOfCollection).doc(nameOfDocument);
console.log("Before starting to load data");
docRef.get().then(function(doc) {
console.log("Got data";
});
console.log("After starting to load data");
When you run this code, the output is:
Before starting to load data
After starting to load data
Got data
This is probably not what you expected, but it's actually completely correct. The data is loaded from Firestore asynchronously (since it may take some time), and instead of waiting, the main code continues. Then when the data is available, your callback function is called with that data.
This means that any code that requires the data from the database must be inside the callback, or be called from there. So the console.log(" Document data:", doc.data()) in your original code should work fine. But a similar console.log outside of the callback won't work, because it runs before the data is available.
This is an extremely common source of confusion for developers new to this type of API. But since most modern web/cloud APIs, and many other APIs, are asynchronous, it's best to learn how to work with them quickly. For that, I recommend reading:
get asynchronous value from firebase firestore reference
Doug's blog post on why Firebase APIs are synchronous
Firestore query in function return
NodeJS, Firestore get field
How do I return the response from an asynchronous call?
The data can be extracted with .data() or .get() to get a specific field.
For example: doc.get(anyfield);
More info can be found on the official documentation.
I have a service that returns me the user's data according to the Token stored in the localStorage and everything is returned correctly until I get to my component.
The real problem is that I have the following code in my component.ts file:
Apparently, at least everything should work out for me. However, in the console it is possible to notice that even after I have assigned the return to my user property it is printed as undefined. As shown in the image below.
When trying to use in the HTML template I get messages saying that it was not possible to load data from the user property. I have already tried using async pipe like this: (user $ | async) as user. I tried to use the safe navigation like this:user?.email.
But it did nothing and I have no idea why this happens. Any help will be welcome!
User$ represents a stream, should be assigned this way:
export class {
// a stream type
user$: Obserable<User>;
ngOnInit() {
// a stream type
this.user$ = this.clienteHeaderService.getUser();
}
}
and the template add |async pipe.
this.user is not immediately available after you call subscribe(), it is very possible that the getUser() hasn't emitted any result by the time console.log(this.user) is called.
If you just wanted to see what's in this.user, you may try it in the callback of subscribe()
this.clientHeaderService.getUser().subscribe((response) => {
this.user = response;
console.log(this.user); // this.user should be available here
})
On the template side, you should be able to just access the user via {{ user }}.
I'd also suggest to use share your minimum reproducible code at https://stackblitz.com/, to get help more easily.
Subscribe almost work like promise in javascript and it has a callback
.subscribe(response=>this.user=response)
Callback are pushed to the end of current event loop .So you are accessing
console.log(this.user)
before callback in your subscribe get executed.
instead
try
.subscribe((response) => {
this.user=response;
//you can access value of this.user here or call other function here to
//access this.user
console.log(this.user);
})
I'm learning React and Firestore currently and am a bit stuck. I'm trying to retrieve a users name from a firestore collection by searching their uid.
The following code is executed in a map of 'lessons' to create a list.
{lesson.post_author && findName(lesson.post_author)}
The following code is the findName function.
let findName = uid => {
firebase.firestore().collection("users")
.where('uid', '==', uid)
.get()
.then(querySnapshot => {
console.log(querySnapshot.docs[0].data().name);
});
};
Currently, the findName function will console log all of the names to the console successfully. I've altered the code to be able to console log outside of the firestore call, but that returns a promise pending in console.
The goal of the code is to return the name rather then the uid in the list.
Any help would be much appreciated.
Thank you!
As others have explained, you can't return that value, since it's loaded from Firestore asynchronously. By the time your return runs, the data hasn't loaded yet.
In React you handle this by putting the data in the component's state, and using it from there. If you do this, your render method can simply pick it up from the state, with something like:
{lesson.post_author && findName(lesson.post_author_name)}
(the above assumes that lesson indirectly comes from the state.
It's a bit easier if we pretend there's only one lesson, and you have these values straight in the state:
{state.post_author && findName(state.post_author_name)}
Now I'll assume you already have the post_author and you just need to look up the author's name. That means that somewhere in/after componentDidMount you'll load the additional data and add it to the state:
componentDidMount() {
firebase.firestore().collection("users")
.where('uid', '==', this.state.uid)
.get()
.then(querySnapshot => {
this.setState({ post_user_name: querySnapshot.docs[0].data().name });
});
}
Now the loading of the data still happens asynchronously, so the call to setState() happens some time after componentDidMount has completed. But React is aware that changing the state may require a refresh of the component, so it responds to the call to setState() by rerendering it.
Note that I'd highly recommend using each user's UID as the ID of the documents in users. That way you don't need a query and can just do a directly lookup:
componentDidMount() {
firebase.firestore().collection("users")
.doc(this.state.uid)
.get()
.then(doc => {
this.setState({ post_user_name: doc.data().name });
});
}
I'm trying to retrieve a users name from a firestore collection by
searching their uid.
This is accomplished by using the asyncronous .get method on a Firestore reference. In your case, you probably have a users collection of firebase.auth().currentUser.uid named documents.
var userRef = firebase.firestore().collection('users').doc(users.uid);
userRef.get().then(function(doc) {
if (doc.exists) {
console.log("Users first name is:", doc.data().firstName);
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
I am trying to store the text that I retrieve from my firebase database, but when I assign datasnapshot.val() to a variable, it seems to have no effect.
var ref = firebase.database().ref().child("Text");
var output = "initial";
ref.on('value',function(datasnapshot){
output = datasnapshot.val();
})
console.log(output);
The console.log still outputs "initial". Why is this?
Firebase APIs are asynchronous, meaning queries for data, (the on() method in your case) will return immediately, and the results will be sent to you callback some time later. Your code is expecting to receive the results immediately, which will not happen. The log line is being executed immediately after the on() method returns, before the results are available to the callback.
Instead of trying to use the results outside the callback, you should fully handle them inside the callback, where they first become available:
ref.on('value',function(datasnapshot){
output = datasnapshot.val();
console.log(output);
})
Please also read this blog post that goes into more detail about Firebase asynchronous APIs, and why they were designed like that.
I'm trying to get the data from my Firebase with AngularFire2.
I want to check specific data and after I get this data from Firebase, I can check it only in the specific scope and not after the operation to Firebase. Why does it happen?
Below is my code:
this.af.database.list('/users/1qfcMAQnglX9jsW5GdLpPko1HqE2', { preserveSnapshot: true})
.subscribe(snapshots=>{
snapshots.forEach(snapshot => {
if(snapshot.key=="reg_boolean"){
console.log(snapshot.val());
this.bo=snapshot.val();
}
this.currentUser.push({key:snapshot.key,value:snapshot.val()});
console.log(this.currentUser);
//console.log(snapshot.key, snapshot.val());
if(this.bo==true){console.log("happy"); }; //i can access only in this scope
});
})
if(this.bo==true){console.log("happy"); }; //why i can access this value??it's undefined, this happen before the subscribe with angularfire2
It's undefined because this.af.database.list it's asynchronous so the code in subscribe will execute when this.af.database.list does retrieve the data. So when the code got to the line if(this.bo==true){console.log("happy"); }; It has nothing because subscribe did not finish at all.
The subscribe it's like the old promise but now it's working with rxjs I recommend you to learn it because angular and ionic has a lot of focus on that.
Try looking at https://www.learnrxjs.io/