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.

firebase-database-users
Firebase database users

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.