I am retrieving data from cloud firestor in componentDidMount() method, but when fetching starts and during fetching data from cloud firestor I am not able to change tab, it takes 8 seconds for less than 100 items docs.
I am using react-navigation and cloud firestor in react-native app.
Even from cache it takes 8 seconds and I couldnt change tabs during fetching data from cloud firestor, but after fetching I am able to change tabs.
getMessages(){
db.collection("users/" + this.state.username + "/msgs").orderBy("date", "asc").get().then(snapshot=>{
this.docs = snapshot.docs;
for (let i = this.docs.length - 1; i >= 0; i--) {
this.prtcpnts = this.state.currentuser === this.docs[i].data().user.username ? this.state.currentuser + this.docs[i].data().otheruser : this.state.currentuser + this.docs[i].data().user.username;
if (this.state[this.prtcpnts] === undefined){
this.setState({
[this.prtcpnts]: [this.docs[i].data()]
});
}else{
this.setState(preState => ({ [this.prtcpnts]: [...preState[this.prtcpnts], this.docs[i].data()] }));
}
}
});
}
I want to fetch smothly without stopping my app, I mean I should be able to change tab even during fetching data from cloud firestor.
I solved my problem by refactoring to the following code.
if (this.outState[this.prtcpnts] === undefined){
this.outState[this.prtcpnts] = [this.docs[i].data()];
}else{
this.outState[this.prtcpnts] = [...this.outState[this.prtcpnts], this.docs[i].data()]
}
if (i === 0) {
this.setState({ ...this.state, ...this.outState })
}
Related
Im trying to do something absurderlly simple but this whole IAM, Realtime Database, cloud functions misconfiguration are making me waste hours of work in something simple as a hello world.
I have an already populated database (over 300 items):
then i have the following function deployed to firebase cloud:
const actions = [];
const igDatabase = require('firebase-admin').initializeApp({
serviceAccountId: 'actionkeeper#igibo-b0b27.iam.gserviceaccount.com'
}).database("https://igibo-b0b27.firebaseio.com/");
let lastMapRefresh = 0;
let lastUpdateFirebase = 0;
function refreshActions(afterRefresh) {
console.log("refreshing actions");
igDatabase.ref('actions/').orderByChild('timestamp').startAt(lastMapRefresh).once('value').then(function(data) {
if (data != null && data.exists()) {
let bef = actions.length;
actions.length = 0;
actions.push(data.val());
lastMapRefresh = new Date().getTime();
afterRefresh();
}
console.log("actions refreshed before: " + bef + " now: " + actions.length);
}).catch(function(error) {
console.error("Error: " + JSON.stringify(error));
});
}
exports.decrementAction = (req, res) => {
refreshActions(function() {});
}
this function is simple reading a branch on database and populating an array... the purpose of the functio is more complex but im building it and testing slowly... and even this simple method is not working
the firebase rules for that node is:
{
"rules":{
"actions":{
".indexOn":[
"timestamp"
],
".read":"auth != null",
"$act":{
"countdown":{
".write":"auth != null && data.val() - newData.val() == 1 && newData.val() >= 0"
}
}
}
}
}
so ANYBODY logged can read
in the google IAM console i have
so the service account is supposed to have admin powers to database...
but running this function ALWAYS RETURN NULL data
why?
Your code doesn't appear to actually send a respond to the client. Here's your function:
exports.decrementAction = (req, res) => {
refreshActions(function() {});
}
Not once does it ever use res to send a response. It's always going to time out after the default 60s, stuck waiting for you to call res.send() or something similar.
I suggest reviewing the documentation on HTTP triggers to see how to send a response. I imagine your (currently empty) callback function needs to do this, based on what refreshFunctions delivers to it.
I was using the query "OnUpdate" on each client to get the data from that node and calculate the children-count but it is too costly.
So I decided to use a cloud-function and create another node of children-count based on the node in which all the users exist but there is an issue, I'm unable to find any query like "OnChildAdded".
The available queries listed on firebase documentation are "OnUpdate", "OnDelete", "OnWrite" and "OnCreate" that are useless for this case because using "OnCreate" on child node cannot return me the children of parent node or "OnUpdate" on parent node will again become costly because all the users update their states frequently.
So what about "OnOperation"? Is there any use of it or is there any other way to reduce the cost of query and also create a children-count node?
Here is the structure of my database
{
currentGame: {
players: {
playerId: {...playerGameData},
//,
},
noOfPlayer: // this is what i wanted to create based on above players node children_count.
}
}
Here is the solution to the above problem in case anyone else need to solve a similar issue.
const PLAYER_REF = "currentGame/players/{playerId}";
const PLAYER_COUNT_NODE = "currentGame/noOfPlayers";
exports.incPlayersCount = functions.database.ref (PLAYER_REF).onCreate (async (snap) =>
{
const countRef = snap.ref.root.child (PLAYER_COUNT_NODE);
await countRef.transaction((current) => {
return (typeof current !== "number" || current < 0) ? 1 : current + 1;
});
return null;
});
exports.decPlayersCount = functions.database.ref (PLAYER_REF).onDelete (async (snap) =>
{
const countRef = snap.ref.root.child (PLAYER_COUNT_NODE);
await countRef.transaction((current) => {
return (typeof current !== "number" || current <= 0) ? 0 : current - 1;
});
return null;
});
btw - it is exactly similar to the sample code that #FrankvanPuffelen have shared in the above comments.
I'm using react-native-firebase in my app. The problem i'm facing is how to handle the UI updates when user tries to push data when offline.
If the user is online we can use the on() method to get realtime updates but what to do when they are offline. We know that the pushed data is stored in the cache and pushed when user is online again. Can this cached data be used to do what i aim at achieving?
Here's the code i used to receive realtime updates:
var ref333 = firebase.database().ref(`/user-posts/${uid}/`)
ref333.on('value',function (snap) {
var s = snap.val();
console.log("NEW POSTS "+JSON.stringify(s))
})
The code i use to push the data.
var postData = {
uid: uid,
body: 'body',
title: 'title',
starCount: 0
};
// Get a key for a new Post.
var newPostKey = firebase.database().ref().child('posts').push().key;
var ref222 = firebase.database().ref(`/posts/${newPostKey}`)
var ref333 = firebase.database().ref(`/user-posts/${uid}/${newPostKey}`)
ref222.push(postData, function (onComplete) {
console.log("COMPLETED")
ref333.push(postData,function (onComplete) {
console.log("NEXT COMPLETED")
}, function (error) {
console.log("ERROR IN ",error)
})
}, function (error) {
console.log("error == "+error)
})
The .on snspashot listener should be triggered even if in offline mode.
According to the docs:
https://firebase.google.com/docs/database/web/read-and-write
You can use the value event to read a static snapshot of the contents
at a given path, as they existed at the time of the event. This method
is triggered once when the listener is attached and again every time
the data, including children, changes.
This should work in offline mode as well. If you are not receiving updates - something else is wrong.
This problem was solved by adding this lines of code to your native code:
https://rnfirebase.io/docs/v5.x.x/core/default-app#Enable-Database-Persistence
I am using AngularFire2 with ionic 2 and storing the data in webSqlStorage.
When I complete the the first GET it saves it to storage. What I want to do is if the user has a connection then check the data from Firebase, if it has not changed, then get the local data, else get the updated data.
Currently, I have a simple check to see if there the storage is not null which then gets the local data, but this will not work for production.
let loader = this.loadingCtrl.create({
content: 'Getting Programs...'
});
loader.present().then(() => {
this.storage.get('programs').then((data) => {
if (data != null) {
loader.setContent("Getting Local Data...");
this.programs = data;
} else {
this.yApi.getPrograms().then(data => {
this.programs = data;
this.storage.set('programs', data);
},err => {
// Probaly offline with no local data
console.log("Err is to human");
});
}
}).then(() => {
loader.dismiss();
});
});
Just wondering if there is a way to write something like
if (data != null || this.af.checkUpdated('/programs')) { ...
or something.
I used an additional database value like program_version in my projects. If firebase program_version is greater than the local storage, than update. On every program update your program_version will be increased or set a timestamp.
I'm working on a presence-like system in firebase with following layout:
firebase {
user1 {
isOnline: true
}
user 2 {
isOnline: true
}
user3 {
isOnline: false
}
}
The isOnline booleans are what I am going to use later to output the names of the users that are online to the console
So for example, in the case above it would say:
user1 is online.
user2 is online.
Here is my code:
var gameRef = new Firebase("https://xxx.firebaseio.com/");
var userOnline = new Firebase('https://xxx/.info/connected');
userOnline.on('value', function (snapshot) {
if (snapshot.val()) {
gameRef.child(user).update({
isOnline : true
});
}
else {
gameRef.child(user).update({
isOnline : false
});
}
});
// for each user that is online, output to the console
gameRef.forEach(function (snapshot) {
var obj = snapshot.val();
if(obj.isOnline == true) {
console.log(obj.name + " is online.");
}
});
There seems to be a problem with my forEach, how can I fix this?
Thanks.
You cannot forEach over a ref, but only over a snapshot.
// for each user that is online, output to the console
gameRef.on('value', function(function(gamesSnapshot) {
gamesSnapshot.forEach(function (snapshot) {
var obj = snapshot.val();
if(obj.isOnline == true) {
console.log(obj.name + " is online.");
}
}
});
This code has two snapshot variables:
gameSnapshot is the data in the parent node
snapshot is the data of a specific player
Alternative
The approach above will download all players, even though you are only looking to deal with players that are online. It is more efficient in this case, to query Firebase so that it only returns players that are online.
// for each user that is online, output to the console
var onlinePlayers = gameRef.orderByChild('isOnline').equalTo(true);
onlinePlayers.on('child_added', function(function(snapshot) {
var obj = snapshot.val();
if(obj.isOnline == true) {
console.log(obj.name + " is online.");
}
});
The code now listens for the child_added event, since Firebase spoon-feeds us the players one at a time. You will probably also have to handle child_changed and child_removed, once you map the players to HTML elements.
Even though this will result in a bit more code, I would normally recommend using querying and the child_* events, since they limit the data that Firebase sends you both initially and when e.g. a player goes offline.