Firebase Firestore onSnapshot получает значение null при использовании значения поля firestore для метки времени

# #reactjs #firebase #react-native #google-cloud-firestore

Вопрос:

Я хочу использовать свое время в firestore для своего приложения для чата. Я закодировал код ниже, но есть проблема со значением поля firestore.

Я отправляю свое сообщение в firestore, как:

 chatsRef.add({
  ...m,
  createdAt: firebase.firestore.FieldValue.serverTimestamp(),
});
 

Все работает правильно, но когда я слушаю(onSnapshot), документ, в котором указано chatsRef createdAt значение, всегда становится нулевым, также когда я проверил, правильно ли установлено время firestore. Я думаю, что там есть асинхронное событие, и я должен его дождаться. Как я могу решить эту проблему?

То chatsRef :

 const chatsRef= db.collection("ChatRooms").doc(id).collection("Chat")
 

Я слушаю чат с этим кодом:

   useEffect(() => {
    let x = null;
    const unsubscribe = chatsRef
      .orderBy("createdAt", "desc")
      .limit(10)
      .onSnapshot((querySnapshot) => {
        const messagesFirestore = querySnapshot
          .docChanges()
          .filter(({ type }) => type === "added")
          .map(({ doc }) => {
            const message = doc.data();
            x = message;
            console.log(x);// I checked my code and the object's createdAt field gets null.
            return { ...message, createdAt: message.createdAt.toDate() };
          });

        appendMessages(messagesFirestore);

       // Some irrelevant codes

    return () => unsubscribe();
  }, []);
 

Также моя структура JSON сообщения такова :

 Object {
  "_id": "a51d2e86-df8d-449e-b41e-73fbd746856f",
  "createdAt": t {
"nanoseconds": 26000000,
"seconds": 1616336752
},
  "text": "1111",
  "user": Object {
    "_id": "jack1212312",
    "name": "jackAndME",
  },
}
 

Последние изменения:

 useEffect(() => {
    let x = null;
    const unsubscribe = chatsRef
      .orderBy("createdAt", "desc")
      .limit(10)
      .onSnapshot({ includeMetadataChanges: true }, (querySnapshot) => {
        const messagesFirestore = querySnapshot
          .docChanges()
          .filter(({ type }) => type === "added")
          .map(({ doc }) => {
            const message = doc.data();

            if (!doc.metadata.hasPendingWrites) {
              x = message;
              return { ...message, createdAt: message.createdAt.toDate() };
            }

            return null;
          });
        if (messagesFirestore[0]) {
          appendMessages(messagesFirestore);

          if (latestMessage.current != null) {
            if (
              new Date(
                latestMessage.current["createdAt"]["seconds"] * 1000  
                  latestMessage.current["createdAt"]["nanoseconds"] / 1000000
              ) >
              new Date(
                x["createdAt"]["seconds"] * 1000  
                  x["createdAt"]["nanoseconds"] / 1000000
              )
            ) {
              latestMessage.current = x;
            }
          } else {
            latestMessage.current = x;
          }
        }
      });

    return () => unsubscribe();
  }, []);
 

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

Док Firebase говорит, что :

 1- A change event is immediately fired with the new data. The document has not yet been written to the backend so the "pending writes" flag is true.
2- The document is written to the backend.
3- The backend notifies the client of the successful write. There is no change to the document data, but there is a metadata change because the "pending writes" flag is now false. 
 

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

Комментарии:

1. Вы уже пытались вызвать функцию с асинхронным ожиданием?

2. Я не понимаю, что вы имеете в виду? Не могли бы вы объяснить немного подробнее? @LaravDev

Ответ №1:

Это связано с тем, что Firebase называет «компенсацией задержки».:

Локальные записи в вашем приложении немедленно вызовут прослушиватели моментальных снимков. Это связано с важной функцией, называемой «компенсация задержки». Когда вы выполняете запись, ваши слушатели будут уведомлены о новых данных до того, как данные будут отправлены в серверную часть.

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

Чтобы провести различие между ними, используйте поле metadata.hasPendingWrites , как описано в документации.

Комментарии:

1. Спасибо за помощь. Теперь я могу обнаружить метаданные, но как я могу получить последнюю версию данных( после написания бэкенда), потому что я могу обнаружить изменение, но не могу получить доступ к данным последней версии. Я также добавил свой последний код. Также я добавил цитату из firebase.

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