[Перевод] Бесконечный скроллинг на Firebase

Из данной статьи вы узнаете, как реализовать бесконечный скроллинг с помощью JavaScript и Firebase, при этом вам не нужно будет что-либо менять в вашей уже существующей структуре данных для Firebase.

Я старался написать данную статью таким образом, чтобы информация в ней была как можно более универсальной и практичной для любого фреймворка/библиотеки, поэтому, я надеюсь, что каждый шаг, который необходим для реализации задумки в ваших проектах, будет максимально понятен.

Заметка: если ознакомительная информация вас не интересует, то можете смело переходить к примерам кода, приведенным ниже.

7zjailca5a5ksya71ajsbfbdxvk.png

Введение


Прежде чем начать давайте определим ожидаемое поведение:

  • Изначальная выборка: возвращает 5 новых элементов;
  • последующие выборки: возвращают последующие 5 новых элементов.


Кроме этого, нам необходимо определить тестовые данные, для которых мы попытаемся реализовать выборку из Firebase: используя такой подход, мне будет проще дать вам наглядный пример того, что происходит с запросами.

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


Push keys для Firebase


Начнем мы с того, что я раскрою вам всю красоту push keys для Firebase, а также расскажу о том, каким образом с их помощью мы реализуем нашу задумку.

Firebase push keys — это не просто набор случайных символов. Оказывается, firebase push keys состоят из комбинации временных меток и случайных данных, закодированных в модифицированном алфавите base64 для сохранения их хронологического порядка (то есть порядка их вставки).

Эта незаменимая особенность позволит нам сортировать запросы Firebase.

Теперь перейдет к непосредственной разработке.

Первый шаг


Для того чтобы все наши выборки, следующие за самой первой выборкой, возвращали именно те данные, которые они должны возвращать, нам необходимо будет создать некоторую ссылку для ключа, который был выбран раньше всех.

Давайте определим переменную следующим образом:

let referenceToOldestKey = ‘’;


Изначальная выборка


Следующим шагом будет создание запроса для выборки первых 5 вложенных элементов:

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


Данный запрос выполняет следующие действия:

  • orderByKey сортирует элементы по их ключам в хронологическом порядке (от последних до первых);
  • limitToLast выбирает 5 самых последних элементов (начиная с 5-го элемента).


Результат будет выглядеть так:

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


Затем нам нужно изменить порядок элементов для полученного результата: последний элемент должен был сверху, а не внизу.

Для данной цели существует несколько вариантов, в зависимости от того, каким образом вы будете управлять состоянием своего приложения:

  • Изменить порядок элементов для полученного результата;
  • преобразовать объект (полученный результат) в массив объектов, а затем изменить сам массив;
  • не изменять объект, но сделать его поверхностную копию и изменить последнюю при ее отображении пользователю.


Мы выберем второй вариант, поскольку он является наиболее распространенным методом для хранения такого рода данных.

Добавляем следующий кусок кода в .then для нашей функции выборки:

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


Далее нам необходимо инициализировать нашу ссылку, начиная с самого последнего из 5 ключей для того, чтобы во время прокрутки пользователем страницы вниз наша следующая функция выборки распознавала требующийся для прокрутки элемент.

referenceToOldestKey = arrayOfkeys[0];


(Самый последний ключ занимает позицию [0], поскольку limitToLast возвращает элементы в хронологическом порядке.)

Итак, мы закончили с первой выборкой.

Следующая выборка


Теперь давайте сделаем так, что при прокрутке пользователем страницы до ее конца активировалась выборка для 5 первых элементов. Для этого мы напишем следующий запрос:

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


Данный запрос выполняет следующее:

  • orderByKey сортирует элементы по их ключам в хронологическом порядке (от последних до первых);
  • endAt выбирает все элементы, начиная с 1-го элемента, который был добавлен в нашу исходную переменную (включающий);
  • limitToLast выбирает 6 элементов с конца (начиная с 6-го).


Результат будет следующим:

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


  • Хм…, а почему мы ограничиваемся 6 последними элементами, а не 5, как в случае с первой выборкой?


Поскольку метод endAt является включающим, т.е. наш исходный ключ включается в возвращаемый объект (или в полученный результат). Таким образом, если бы мы ограничились только 5 элементами, то у нас получилось бы задействовать только 4 новых элемента: 1 был бы дубликатом. Необходимо делать запрос для 6 элементов, а затем удалить 6-й элемент на стороне клиента.

Теперь, когда мы разобрались с тем, как работает запрос, нам необходимо отменить возвращенный объект и удалить дубликат.

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


Почему slice (1)? После reverse () наша копия перемещается с последней позиции на первую. Как понять, что копия была на последней позиции? Все ключи возвращаются в хронологическом порядке.

Наконец, для ссылки нам нужно написать значение последнего ключа из текущей выборки.

referenceToOldestKey = arrayOfkeys[0];


Таким образом, все необходимое для последующей выборки сделано.

На этом мое краткое руководство заканчивается. Спасибо всем читающим за ваше внимание, и я очень надеюсь, что вы подчерпнули что-то новое для себя из моей статьи. Внизу представлен полный пример кода.

Полный пример кода


Обратите внимание: дублирование кода внутри .then для обоих запросов было показано здесь для лучшего восприятия кода. Так, для нахождения нужного фрагмента вам не нужно штудировать весь код целиком, как, например, в случае с размещением кода в отдельную функцию.

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) => { … } );


 
 }


Я очень надеюсь, что данная статья не оказалась для вас пустой тратой времени, и вы нашли в ней что-то полезное для себя!

© Habrahabr.ru