i trying to get the data from my database, in componentWillMount(), it works fine with this :
var userData = null
firebase.database().ref('/users/' + user.uid).once('value').then(function(snapshot) {
userData = snapshot.val()
console.log(userData)
});
But it only works in the method only, i tried to asign the value to a variable but i can't get it outside even with this.setstate.
I am really lost it looks easy but i don't know how ...
Thanks for help.
once() is asynchronous and returns immediately with a promise. The promise does not block your code when you attach a then callback to it. userData won't be populated in the callback from the promise until after the database query completes, and you have no guarantee when that will be. If your code tries to access userData before it's finally populated, it will still have its original null value.
Well, what's happening here is,
your firebase method is taking a time to get data and because everything is asynchronous here, javascript will not wait until your firebase method is executed.
Immediate next line of firebase.database().... will be executed before this completes.
So you might need to set your data into state using setState or you can use a class variable instead of a local variable.
do this,
constructor(props){
super(props);
this.state={
data:null //either do this
}
this.localData=null; // or this
}
firebase.database().ref('/users/' + user.uid).once('value').then(function(snapshot) {
this.setState({data:snapshot.val()});
this.localData = snapshot.val();
console.log(this.localData)
});
Related
I cannot get SvelteKit load function works when using it with Firebase, I always get this error message:
a load function related to route '/' returned a function, but must return a plain object at the top level (i.e. return {...})
I'm using onSnapshot here with Firestone to get the updated data whenever it changed on the database.
export function load() {
const queryParams = [orderBy('date')];
const q = query(collection(db, 'daily_status'), ...queryParams);
messagesUnsubscribeCallback = onSnapshot(
q,
querySnapshot => {
let data = querySnapshot.docs.map( doc => (
JSON.parse(JSON.stringify(
{
id: doc.id,
status: doc.data().status,
date: doc.data().date.toDate().toLocaleDateString('en-au'),
note: doc.data().note
}
))
))
return { daily_status: data }
})
return messagesUnsubscribeCallback;
}
It looks like your issue is the fact that you are returning the function onSnapshot() inside the load function. The only thing you can return inside a load method is a plain object as the error states. What you might want to do is to run the snapshot code inside an onMount.
Another solution would be creating a svelte store and pass the onSnapshot into the store. An example can be seen in this tutorial:
https://www.captaincodeman.com/lazy-loading-and-querying-firestore-with-sveltekit#introduction
Reference:
https://kit.svelte.dev/docs/load
Your load() function needs to run asynchronous code, so it can't return back the data directly. Instead, you need to change it to return a promise that resolves to the loaded data. For an example using fetch(), see:
https://kit.svelte.dev/docs/load#making-fetch-requests
In your case, you need to create your own promise.
Further more, the purpose of the load() function is to load the initial data the page needs to be able to render. As suggested by another, to subscribe to updates in the future, do that in the page component in onMount(), so you only subscribe to future updates when the component is rendered in the web browser.
I am trying to do an app on react-native with a feed. On my main screen, I go fetch the data :
fetchData() {
firebase.database().ref(`/posts/${group}`).on('value', async snapshot => {...}
}
when I want for example to like a comment of the post, I first push the data into firebase with different queries, for example :
export const likeComment = (...) => {
firebase.database().ref(`/posts/${group}/${post}`).update
({
updatedAt: firebase.database.ServerValue.TIMESTAMP
});
firebase.database().ref(`/posts/${group}/${post}/lastComments`).set(...);
But I realized that my first function fetchData was called 3 times.
then I grouped my queries like :
let updates = {}
updates[`/posts/${group}/${post}/lastComments`] = {...};
updates[`/posts/${group}/${post}`] = { ... };
firebase.database().ref().update(updates);
Then, fetchData was called still 2 times.
I was wondering if that was the best way to do it, and why does my function fetchData was still called twice.
Thanks for your help
It's hard to tell from the limited code, but my guess is that fetchData is not actually being called more than once, but instead the snapshot is firing when you make updates to your Firebase database.
The part of your code where you say .on('value', async snapshot => you're setting up a listener to send you updates any time that value changes.
So my guess is that your three invocations are:
The first time you actually call fetchData and set up the
listener
Your .on( snapshot listener firing when you call
update
Your .on( snapshot listener firing again when you
call set
This push-based database workflow is one of the main benefits of Firebase. If you only want to get the data once and not get live updates, you can call .once( instead of .on(
https://firebase.google.com/docs/database/web/read-and-write
I have a cloud function that is triggered when an account is created. This function populates a few fields in the user's Firebase Database entry. One of the fields is named 'accntActive', and triggers a second cloud function to do some other tasks.
In the second cloud function that gets triggered on a change of the accntActive field, the first thing I do is read the value of accntActive to check if it was changed to the value 'true'. What I am seeing is that about 50% of the time, the cloud function crashes when I try to use this value, saying it is undefined/null. Up until a week ago this always worked fine, 100% of the time, so the code itself has not changed. When I examine the user's DB entry at a later point in time, 100% of the time it has been populated correctly.
I am wondering if there is some race condition, where the DB cloud function is triggered before the value that got written is stable, and thus cannot be read immediately. This would seem a bit ridiculous, given the entire point of the DB cloud function is to respond to the value that is being changed.
The code for the second function:
exports.myActiveFxn = functions.database.ref('/users/{uid}/accntStatus/active')
.onWrite(event => {
const accntActive = event.data.val();
const userID = event.params.uid;
console.log(accntActive.toString()) //This throws an exception since accntActive is sometimes undefined
In the above, note that the userID variable is always stable. I've even tried re-reading the value once inside the function, and I get the same result:
admin.database().ref('/users/'+userID+'/accntStatus/active').once('value').then(function (snapshot) {
var accntActive = snapshot.val();
console.log(accntActive.toString());
}).then(function() {
Does anybody have any insight into what I can do to fix this? Thank you in advance!
Edit: Here is the code for the first function
exports.authUserCreated = functions.auth.user().onCreate(function(event) {
var UID = event.data.uid;
admin.database().ref('/users/'+UID).set({
email: event.data.email,
accntStatus: {
active: "true",
key: randomstring.generate(22)
},
uid: UID
});
return;
});
In your function, you aren't returning a promise that resolves when the work is complete. If you don't do that, strange and inconsistent things may happen. Please read the documentation on async programming with Cloud Functions.
You should return from the function the promise that's returned by the set() method on the database ref.
exports.authUserCreated = functions.auth.user().onCreate(function(event) {
var UID = event.data.uid;
return admin.database().ref('/users/'+UID).set({
email: event.data.email,
accntStatus: {
active: "true",
key: randomstring.generate(22)
},
uid: UID
});
});
I have an issue with my cloud functions where in all my database events all return empty. For example, in the following event the event.data.val() would return null. I am doing an update operation and have tested the update by testing the cloud function using the shell as well as after deploying.
export const createSubscription = functions.database.ref('/users/{userId}/subscription').onWrite( event => {
if(!event.data.val()) {
return;
}
});
But I can easily hook into the auth.user() events like the following and receive the data.
export const createStripeUser = functions.auth.user().onCreate(event => {
const user = event.data;
});
Edit: Passing data into the collection for example like the one below on the emulator console
createSubscription({
testKey: 'testValue'
})
or the following on from my frontend
db.ref(`/users/23213213213/subscription`).update({ testKey: 'testValue'});
would return null on the function.
DougStevenson is correct. For the .onCreate() you would be doing it correct wit a myDatabaseFunction('new_data').
With the .onWrite() you need to pass in the before and after like my example below.
You may have got stuck were I did. Note that I have a curly bracket around the before and after the final JSON. It didn't work properly without them.
addComment({before: {"comment":"before comment","role":"guest"}, after:{"comment":"After Comment","role":"guest"}})
Hope my example helps a bit more than a generic string parameter.
Good Luck!
I have no issues when using implicit updates (angelFire). However I need for some of my data use explicit updating. So I implemented angelFireCollection on the exact same ref I was using previously but despite the console.log explicitly saying that the read was granted and trying it with both with the onloadcallback and without, I don't get data directly into my assigned variable AND once the callback fires I get a strange looking object that DOES contain the data but not in the form I expect. My scope variable ends up with an empty collection. Never gets populated. Here is the code:
var streamController = function ($rootScope, $scope, $log, $location, angularFireCollection, profileService) {
//Wait for firebaseLogin...
$rootScope.$watch('firebaseAuth', init);
function init() {
if ($rootScope.firebaseAuth == false) {
return
};
var refUsers = new Firebase($rootScope.FBURL+'/users/'+$rootScope.uid);
$scope.profile = angularFireCollection(refUsers, function onload(snapshot) {
console.log(snapshot)
});
};
};
myApp.gwWebApp.controller('StreamController', ['$rootScope', '$scope', '$log', '$location', 'angularFireCollection', 'profileService',
streamController]);
}());
Here is what the console.log looks like ( ie; what snapshot looks like ):
>snapshot
T {z: R, bc: J, V: function, val: function, xd: function…}
Here is the earlier message before the snapshot was returned:
Firebase Login Succeeded! fbLoginController.js:16
FIREBASE: Attempt to read /users/529ccc5d1946a93656320b0a with auth={"username":"xxxxxxx#me.com","id":"529ccc5d1946a93656320b0a"} firebase.js:76
FIREBASE: /: "auth.username == 'admin'" firebase.js:76
FIREBASE: => false firebase.js:76
FIREBASE: /users firebase.js:76
FIREBASE: /users/529ccc5d1946a93656320b0a: "auth.id == $user" firebase.js:76
FIREBASE: => true firebase.js:76
FIREBASE:
FIREBASE: Read was allowed.
and finally the desired binding that ends up with an empty array: again from the console:
$scope.profile
[]
Anyone know what I could possibly be doing wrong?? This is like 5 lines of code. Frustrating.
I have put stops in angelFireCollection factory function and can see that the data is getting added to the collection in the callbacks inside that function but my binded variable never gets updated.
UPDATE
Ok experimenting with a plnkr. It seems that angularFireCollection EXPECTS your returning a LIST of items. The snapshot returns properly if you inspect snapshot.val() it will be whatever object structure was stored in firebase. IF you use angularFireCollection it does indeed bind to the variable HOWEVER it turns a non-list object into a garbled mess and you can not access the object user the normal dot operator. This is either a bug or it is a severe limitation of angularFireCollection which will cause me to revaluate how easily I can use firebase as the backend. I can't share my plnkr because it is accessing non-public data but tomorrow if i have time I will create a public firebase with an object store and demonstrate.
Ok. So it appears that indeed angularFireCollection is MEANT to be array based. Which is fine. It would be VERY helpful if the angularFire documentation was updated to make that clear. As such it is not an implicit vs explicit update technique.
For an explicit non-array based approach I have come up with the following code. Had I not been mislead by the documentation I would have gone down this path originally.
var MainCtrl = function($scope, angularFire) {
$scope.test = {};
var _url = 'https://golfwire.firebaseio.com/tmp';
var _ref = new Firebase(_url);
var promise = angularFire(_ref, $scope, 'implicit');
promise.then ( function(data){
$scope.explicit=angular.copy($scope.implicit );
});
}
You then work locally with the 'explicit' copy and when ready just update the 'implicit' by assigning: $scope.implicit = $scope.explicit.
Here is a plnkr: http://plnkr.co/edit/bLJrL1