Endless scrolling on Firebase

Original author: Linas M
  • Transfer
  • Tutorial
In this article, you will learn how to implement infinite scrolling using JavaScript and Firebase, without the need to change anything in your existing data structure for Firebase.

I tried to write this article in such a way that the information in it was as universal and practical as possible for any framework / library, therefore, I hope that every step that is necessary to implement the idea in your projects will be as clear as possible.

Note: if the introductory information does not interest you, then you can safely proceed to the code examples below.



Introduction


Before we begin, let's define the expected behavior:

  • Initial selection: returns 5 new elements;
  • subsequent selections: return the next 5 new items.

In addition, we need to define test data for which we will try to implement a selection from Firebase: using this approach, it will be easier for me to give you a clear example of what happens with the requests.

// наша база данных Firebase 
items: { 
   firstInsertedItem: { … }, // oldest
   SecondInsertedItem: { … }, 
   ThirdInsertedItem: { … },
   FourthInsertedItem: { … }, 
   FifthInsertedItem: { … }, 
   SixthInsertedItem: { … }, 
   SeventhInsertedItem: { … },
   EighthInsertedItem: { … }, 
   NinethInsertedItem: { … }, 
   TenthInsertedItem: { … }, // newest
}
// элементы выше могу хранится в любом порядке 

Push keys for Firebase


We will start with the fact that I will reveal to you the beauty of push keys for Firebase, and also talk about how we use them to realize our idea.

Firebase push keys are not just a collection of random characters. It turns out that firebase push keys consist of a combination of timestamps and random data encoded in a modified base64 alphabet to preserve their chronological order (i.e. the order in which they are inserted).

This indispensable feature will allow us to sort Firebase requests.

Now proceeds to direct development.

First step


In order for all of our samples following the very first sample to return exactly the data that they should return, we will need to create some link for the key that was selected before everyone else.

Let's define a variable as follows:

let referenceToOldestKey = ‘’;

Initial sample


The next step is to create a query to select the first 5 nested elements:

firebase.database().ref(‘items’)
.orderByKey()
.limitToLast(5)
.once(‘value’)
.then((snapshot) => { … } )
.catch((error) => { … } );

This request performs the following actions:

  • orderByKey  sorts items by their keys in chronological order (from last to first);
  • limitToLast  selects the 5 most recent items (starting at the 5th item).

The result will look like this:

{ 
SixthItemFromTheEnd: { … },
SeventhItemFromTheEnd: { … },
EighthInsertedItem: { … },
NinethInsertedItem: { … },
TenthInsertedItem: { … },
}

Then we need to change the order of the elements for the result: the last element was on top, not bottom.

For this purpose, there are several options, depending on how you will manage the state of your application:

  • Change the order of elements for the result;
  • convert the object (the result) into an array of objects, and then change the array itself;
  • Do not modify the object, but make a surface copy of it and change the latter when it is displayed to the user.

We will choose the second option, since it is the most common method for storing this kind of data.

Add the following piece of code to .then for our fetch function:

let arrayOfkeys = Object.keys(snapshot.val());
let results = arrayOfkeys
	.map(key => snapshot.val()[key])
	.reverse();

Next, we need to initialize our link, starting with the most recent of the 5 keys, so that when the user scrolls down the page, our next selection function will recognize the element required for scrolling.

referenceToOldestKey = arrayOfkeys[0];

(The most recent key is at position [0], since limitToLast returns the elements in chronological order.)

So, we are done with the first selection.

Next sample


Now let's make sure that when the user scrolls to the end of the page, the selection for the first 5 elements is activated. To do this, we will write the following query:

firebase.database().ref(‘items’)
.orderByKey()
.endAt(referenceToOldestKey)
.limitToLast(6)
 .once(‘value’)
.then((snapshot) => { … } )
.catch((error) => { … } );

This query does the following:

  • orderByKey  sorts items by their keys in chronological order (from last to first);
  • endAt selects all elements starting from the 1st element that was added to our original variable (including);
  • limitToLast  selects 6 elements from the end (starting from the 6th).

The result will be as follows:

{ 
firstInsertedItem: { … },
SecondInsertedItem: { … },
ThirdInsertedItem: { … },
FourthInsertedItem: { … },
FifthInsertedItem: { … },
SixthInsertedItem: { … },  
}

  • Hmm ... why are we limited to the last 6 elements, and not 5, as is the case with the first sample?

Since the endAt method  is inclusive, i.e. our source key is included in the returned object (or in the result). Thus, if we limited ourselves to only 5 elements, then we would be able to use only 4 new elements: 1 would be a duplicate. It is necessary to make a request for 6 elements, and then delete the 6th element on the client side.

Now that we’ve figured out how the query works, we need to cancel the returned object and remove the duplicate.

let arrayOfkeys = Object.keys(snapshot.val());
let results = arrayOfkeys
.map(key => snapshot.val()[key])
.reverse()
.slice(1);

Why slice (1) ? After reverse (), our copy moves from the last position to the first. How to understand that the copy was in last position? All keys are returned in chronological order.

Finally, for the link we need to write the value of the last key from the current selection.

referenceToOldestKey = arrayOfkeys[0];

Thus, everything necessary for the subsequent selection is done.

This concludes my short guide. Thanks to everyone reading for your attention, and I really hope that you have learned something new for yourself from my article. Below is a complete code example.

Full code example


Please note: code duplication inside .then  for both requests has been shown here for a better perception of the code. So, to find the desired fragment, you do not need to study the entire code as a whole, as, for example, in the case of placing the code in a separate function.

let referenceToOldestKey = ‘’;
if (!referenceToOldestKey) {
      firebase.database().ref(‘items’)
   .orderByKey()
   .limitToLast(5)
         .once(‘value’)
   .then((snapshot) => { 
            let arrayOfkeys = Object.keys(snapshot.val());
            let results = arrayOfKeys
               .map(key => snapshot.val()[key])
         .reverse();
            // хранение ссылки
      referenceToOldestKey = arrayOfkeys[0];
            // Можете работать с данными, например
            // добавить на страницу ({ … }) если использовать redux
         })
         .catch((error) => { … } );
 } else {
  firebase.database().ref(‘items’)
         .orderByKey()
   .endAt(oldestKeyReference)
   .limitToLast(6)
         .once(‘value’)
         .then((snapshot) => {
            let arrayOfkeys = Object.keys(snapshot.val());
      // преобразование в массив &    
     // обратный порядок     
      // удаление дубликатов &
      let results = arrayOfkeys
         .map(key => snapshot.val()[key])
.reverse()
         .slice(1);
      // обновление ссылки
      referenceToOldestKey = arrayOfkeys[0];
     // Можете работать с данными, например
     // добавить на страницу ({ … }) если использовать redux
   })
   .catch((error) => { … } );
 }

I really hope that this article was not a waste of time for you, and you found in it something useful for yourself!

- Best promotion service on reddit, bitcointalk and medium: reddit-marketing.pro

Also popular now: