OnChildAdded called multiple times – Firebase
If you are working in Firebase, then you might have encountered an issue with child_added event or onChildAdded method. It is called multiple times on page load. However, it should have been called only when a new child is added. As per Firebase documentation, this is the expected behavior. It first returns all the existing childs and then listen for new added children. But there might be a scenario where you want to perform some action only on new child, not on existing children.
So I have a Firebase database and I have a users array in it.
Step 1
The way I got around this problem, is by first I am looping through all the records in Firebase.
<script type="module">
// Import the functions you need from the SDKs you need
import { initializeApp } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-app.js";
import { getDatabase, ref, get, onChildAdded } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-database.js";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: ""
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const database = getDatabase(app);
const usersRef = ref(database, "users")
// [step 2]
get(usersRef).then(function (snapshot) {
if (snapshot.exists()) {
snapshot.forEach(function (childSnapshot) {
// [step 3]
})
}
})
</script>
Note: Make sure to use your own Firebase configurations. If you do not know how to setup Firebase configurations, you can follow our guide.
Step 2
After that, we will create a variable that will count the length of users array. Write the following line in place of // [step 2]
.
let count = 0
This will initialize the variable “count”.
Step 3
Then increment that variable’s value by 1. Following code goes in place of // [step 3]
.
count++
if (snapshot.size == count) {
// [step 4]
}
This will also checks when all the element of array has been looped.
Step 4
Initialize another variable that will tell if all the elements has been fetched.
let hasInitializedFirebase = false
Then we will set its value to true at // [step 4]
.
setTimeout(function () {
hasInitializedFirebase = true;
}, 1000)
We are adding a delay of 1 second to prevent the onChildAdded event to be called immidiately.
Step 5
Last step is to call the onChildAdded event and check if the variable hasInitializedFirebase is true.
onChildAdded(usersRef, async function (data) {
if (hasInitializedFirebase) {
console.log(data)
}
})
If you reload the page now, you will not see the onChildAdded logs, but as soon as you insert a new element in array, you will see that the console.log(data)
has been called.
Complete code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Test</title>
</head>
<body>
<script type="module">
// Import the functions you need from the SDKs you need
import { initializeApp } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-app.js";
import { getDatabase, ref, get, set, push, onChildAdded, onChildChanged } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-database.js";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: ""
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const database = getDatabase(app);
const usersRef = ref(database, "users")
let count = 0
let hasInitializedFirebase = false
get(usersRef).then(function (snapshot) {
if (snapshot.exists()) {
snapshot.forEach(function (childSnapshot) {
count++
if (snapshot.size == count) {
setTimeout(function () {
hasInitializedFirebase = true;
}, 1000)
}
})
} else {
setTimeout(function () {
hasInitializedFirebase = true;
}, 1000)
}
})
onChildAdded(usersRef, async function (data) {
if (hasInitializedFirebase) {
console.log(data)
}
})
</script>
</body>
</html>
That’s how you can prevent onChildAdded event to be called multiples times in Firebase. If you face any problem in this, feel free to contact me from chat widget on bottom right.