Update and increment value with realtime database - javascript

Im trying to update the points of the child : add 40points to the old result points, but I get the error :
undefined is not an object (Evaluating 'pointRef.update')
const pointRef=firebase.database().ref("Users").child(firebase.auth().currentUser.uid)
.once("value",(snapshot=>
{
pointRef.update({Points:snapshot.val().Points+40
})

You are using .once() method on pointRef so it is no longer a DatabaseReference. It is a DataSnapshot now. So you cannot call the .update() method on it. Instead try using the following code:
const pointRef = firebase.database().ref("Users").child(firebase.auth().currentUser.uid)
pointRef.once("value").then((snapshot) => {
pointRef.update({Points:snapshot.val().Points+40}).then(() => {
console.log("Points updated")
})
})
The better way of doing this:
firebase.database().ref("Users").child(firebase.auth().currentUser.uid)
.set({Points: firebase.database.ServerValue.increment(40)})
Here you don't need to manually fetch, add and update the points field.

Related

Firestore: How to update array on a condition and nested array

Image of database
I can't seem to modify the convos array on a condition, let alone update the nested values.
Two things I want to do:
Change 'connected' to false given a socketID.
Add an object into messages array given a socketID.
I'm able to add into the convos array and read from the convos array just fine, but I can't update or change anything.
Below is the code which I think was my best attempt
const person = {socketID: '123123'};
firebase.firestore().collection("Conversations").doc("x0uio6wWlxEyos6ruc2r")
.onSnapshot((doc)=> {
doc.where('socketID', '===', person.socketID)
.update({
connected: false
})
})
I think if I correctly know how to access the convos array inside a specific document, then I should be able to make update changes accordingly.
Any help is greatly appreciated.
Try this,
update({
connected : false,
messages : firebase.firestore.FieldValue.arrayUnion(object)
})
And to remove element use arrayRemove
You cannot apply filtering on DocumentData. You may need to manipulate the document over javascript.
const data = (await firebase.firestore().collection("Conversations").doc("x0uio6wWlxEyos6ruc2r").get()).data();
const manipulatedData = data.find(x => x.socketID == person.socketID).connected = false;
firebase.firestore().collection("Conversations").doc("x0uio6wWlxEyos6ruc2r").set(manipulatedData, { merge: true });
You may need to make small changes to the codes.

React Native w/ firebase "undefined is not an object"

Getting this error:
TypeError: undefined is not an object (evaluating 'doc.data().books')
from this code:
const getData = () => {
const snapshot = db.collection('users').doc(userEmail).onSnapshot((doc) => {
setData(doc.data().books)
})
}
useEffect(() => {
getData()
}, [])
this is the firestore database, trying to get to the updated boo collection (gets shown in a flatlist that needs to get updated when new content is added to the collection) Note that there's a users collection that is offscreen.
I'm pretty sure it's something that I'm doing wrong on my snapshot but I'm really not sure what, all help is greatly appreciated :)
The error is telling you that doc.data() returned undefined, which means the document you requested doesn't exist. You probably passed a value for userEmail other than what you expected it to be. Since you didn't show the value, we don't know for sure what went wrong.
I will point out that document IDs must be exact matches, so if the email address wasn't exactly "Email#email.com" with a capital E, the query will not return a document using what you've shown here.
There are several possibilities. However, db is not defined within the scope of the getData function... where is it defined?
If I correctly understand your question, you want to get the documents in the books (or boo?) subcollection of the user document with id corresponding to userEmail.
If this assumption is correct, you should do as follows:
const getData = () => {
db.collection('users').doc(userEmail).collection('books')
.onSnapshot(querySnapshot => {
var books = [];
querySnapshot.forEach(doc => {
books.push(doc.data());
});
// Here you have an array of all the book objects.
// It's up to you to use it as you wish with your setData() function
});
}
Note that with doc.data().books you are actually trying to get the books field of the user doc.

Javascript saying object value is undefined. What am I doing wrong?

I'm trying to display the value of an object within an array of Github repositories (retrieved from a parsed JSON) in my React app but I'm getting an error that the browser can't read property key of undefined.
I'm able to console.log the JSON data and the objects within it but if I try to log the values within those objects, I get the error.
const App = () => {
const [repoList, setRepoList] = useState({});
useEffect(() => {
fetch("https://example.com/api")
.then(res => res.json())
.then(data => setRepoList(data));
});
return (
<div>{repoList[0].id}</div>
)
}
If I put JSON.stringify(repoList) within the div element, it shows the data.
If I put JSON.stringify(repoList[0]) within the div element, it also shows the data.
But if I put something like repoList[0].id nothing appears. This happens with each key listed. And I can confirm that the key is there. What am I doing wrong?
Try this.
const [repoList, setRepoList] = useState([]); // Change to array not object.
...
return repoList.length ?
(<React.Fragment>{repoList[0].id}</React.Fragment>)
: (<span>Loading ....</span>)
If your div does not have styles, better user Fragment, it result in better performance.
You're calling async functions to populate the repo list. The first time the return tries to use repoList[0].id, there is no repoList[0]. You may want to initialize repoList to a non-error-ing dummy object when you set it up with useState({})

Reading data after writing to it in Firebase

I am trying to navigate to a specific ID after creating an item in my realtime database.
The documentation offers a solution but it is not working as shown.
After using the push().set() method on my db ref I tried creating a callback function that would get the specific ID created using the push.().set() method and then navigate with that ID.
db.ref.push().set({
...
}).then(function(){
router.navigate(ref/ + db.ref.key /)
})
After creating a new object at that ref point I want to use the ID created by firebase to navigate to a specific route using that ID.
The issue is that db.ref.push() returns a new Reference object with the unique id to it. You're not getting the value of that ID. You can refactor things so it would be something like:
let newref = db.ref.push();
newref.set({
...
})
.then( () => {
// You can use "newref" here
// The last part of the full reference path is in "key"
let id = newref.key;
// Do something with the id as part of your route
});
set
set(value, onComplete) => returns firebase.Promise containing void
So you are absolutely correct about that you should be able to see the operation as resolved with a callback however the promise will not give you any key back.
You should however be able to chain this event:
db.ref.push().set({
...
}).getKey().then(data => console.log(data))

Firebase child_added for new items only

I am using Firebase and Node with Redux. I am loading all objects from a key as follows.
firebaseDb.child('invites').on('child_added', snapshot => {
})
The idea behind this method is that we get a payload from the database and only use one action to updated my local data stores via the Reducers.
Next, I need to listen for any NEW or UPDATED children of the key invite.
The problem now, however, is that the child_added event triggers for all existing keys, as well as newly added ones. I do not want this behaviour, I only require new keys, as I have the existing data retrieved.
I am aware that child_added is typically used for this type of operation, however, i wish to reduce the number of actions fired, and renders triggered as a result.
What would be the best pattern to achieve this goal?
Thanks,
Although the limit method is pretty good and efficient, but you still need to add a check to the child_added for the last item that will be grabbed. Also I don't know if it's still the case, but you might get "old" events from previously deleted items, so you might need to watch at for this too.
Other solutions would be to either:
Use a boolean that will prevent old added objects to call the callback
let newItems = false
firebaseDb.child('invites').on('child_added', snapshot => {
if (!newItems) { return }
// do
})
firebaseDb.child('invites').once('value', () => {
newItems = true
})
The disadvantage of this method is that it would imply getting events that will do nothing but still if you have a big initial list might be problematic.
Or if you have a timestamp on your invites, do something like
firebaseDb.child('invites')
.orderByChild('timestamp')
.startAt(Date.now())
.on('child_added', snapshot => {
// do
})
I have solved the problem using the following method.
firebaseDb.child('invites').limitToLast(1).on('child_added', cb)
firebaseDb.child('invites').on('child_changed', cb)
limitToLast(1) gets the last child object of invites, and then listens for any new ones, passing a snapshot object to the cb callback.
child_changed listens for any child update to invites, passing a snapshot to the cb
I solved this by ignoring child_added all together, and using just child_changed. The way I did this was to perform an update() on any items i needed to handle after pushing them to the database. This solution will depend on your needs, but one example is to update a timestamp key whenever you want the event triggered. For example:
var newObj = { ... }
// push the new item with no events
fb.push(newObj)
// update a timestamp key on the item to trigger child_changed
fb.update({ updated: yourTimeStamp })
there was also another solution:
get the number of children and extract that value:
and it's working.
var ref = firebaseDb.child('invites')
ref.once('value').then((dataSnapshot) => {
return dataSnapshot.numChildren()
}).then((count) =>{
ref .on('child_added', (child) => {
if(count>0){
count--
return
}
console.log("child really added")
});
});
If your document keys are time based (unix epoch, ISO8601 or the firebase 'push' keys), this approach, similar to the second approach #balthazar proposed, worked well for us:
const maxDataPoints = 100;
const ref = firebase.database().ref("someKey").orderByKey();
// load the initial data, up to whatever max rows we want
const initialData = await ref.limitToLast(maxDataPoints).once("value")
// get the last key of the data we retrieved
const lastDataPoint = initialDataTimebasedKeys.length > 0 ? initialDataTimebasedKeys[initialDataTimebasedKeys.length - 1].toString() : "0"
// start listening for additions past this point...
// this works because we're fetching ordered by key
// and the key is timebased
const subscriptionRef = ref.startAt(lastDataPoint + "0");
const listener = subscriptionRef.on("child_added", async (snapshot) => {
// do something here
});

Categories

Resources