Использовать firebase onSnapshot() в цикле for?

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

const [activityDataArray, setActivityDataArray] = useState([]);
const userActivityIds = userData.activities

useEffect(() => {
    let promises = [];
    for (const activityId of userActivityIds) {
        promises.push(getFirestoreData("activities", activityId));
    }
    Promise.all(promises).then(response => setActivityDataArray(response));
}, [userActivityIds]);

ОБНОВЛЕННЫЙ КОД: Когда я console.info() массив, у него есть мои данные, но я думаю, что это уловка с инструментами разработчика Chrome, показывающими новую информацию. Я думаю, что когда я вызываю setActivityDataArray, он запускает его на пустом массиве, а затем больше никогда не вызывается. Таким образом, данные не отображаются, если я не переключусь на другую вкладку в своем приложении и не вернусь назад. Затем он отображается правильно (поэтому я знаю, что данные хорошие, это просто проблема рендеринга). Я думаю, мне нужно перерендерить внутри onSnapshot(), но как мне это сделать правильно?

const [activityDataArray, setActivityDataArray] = useState<any>([]);
const userActivityIds: string[] = userData.activities

useEffect(() => {
    let activityDataArrayDummy: any[] = []
    for (const i in userActivityIds) {
        firebase.firestore().collection("activities").doc(userActivityIds[i])
            .onSnapshot((doc) => {
                activityDataArrayDummy[i] = doc.data();
            });
    }
    console.info("activityDataArrayDummy", activityDataArrayDummy)
    setActivityDataArray(activityDataArrayDummy);
}, [userActivityIds]);
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
43
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Простой вызов onSnapshot() в цикле должен сделать это.

import { doc, onSnapshot } from "firebase/firestore";

for (const activityId of userActivityIds) {
  // get reference to document
  const docRef = doc(db, "activities", activityId)
  
  onSnapshot(docRef, (snapshot) => {
    // read and render data from snapshot
  })
}

Однако, если вам когда-нибудь понадобится отписаться от любого из слушателей, вам, возможно, придется сохранить функцию Unsubscribe, возвращаемую onSnapshot. где-то в штате.


На всякий случай, если у вас есть 10 или меньше элементов в userActivityIds, вы можете вместо этого использовать onSnapshot() с Query:

const q = query(collection(db, "activities"), where(documentId(), "in", userActivityIds));

onSnapshot(q, (querySnapshot) => {
  // ... 
})

Так я вроде так и делаю. У меня есть setActivityDataArray([...activityDataArray, doc.data()]) в цикле, но они перезаписывают друг друга.. т.е. рендерится только последний

esafresa 24.03.2022 08:01

@esafresa да, так что все onSnapshot() будут работать независимо друг от друга. Возможно, было бы лучше отправить данные из них в массив, а затем передать их в setActivityDataArray, чтобы состояние обновлялось только один раз, а не для каждого снимка. Можете ли вы попробовать сделать это, добавив массив прямо перед циклом for?

Dharmaraj 24.03.2022 08:07

во-первых, я очень ценю вашу помощь :) не могли бы вы уточнить, что вы имеете в виду? Я думаю, мне нужно вызвать setActivityDataArray изнутри onSnapshot (т.е. onNext), иначе он не будет отображать обновления данных, верно? Я сейчас пытаюсь setActivityDataArray({...activityDataArray, ...copiedActivityObj}), то есть {...prevState, ...newObj}, но я думаю, что это не работает в реакции, объекты перезаписывают друг друга

esafresa 24.03.2022 08:58

Я понял. Теперь я понимаю, что вы имеете в виду. Благодарю вас! :D

esafresa 24.03.2022 09:09

Ах, подождите... это глючит. Я думаю, мне нужно вызвать setState внутри onSnapshot onNext, иначе он не отобразится. Предложения?

esafresa 24.03.2022 09:19

@esafresa я имел в виду, что вы можете объявить массив до начала цикла for, а затем поместить документ в этот массив. Когда у вас есть все документы, используйте setState.. можете ли вы поделиться своим обновленным кодом?

Dharmaraj 24.03.2022 09:22

Хорошо, добавил обновленный код :)

esafresa 24.03.2022 09:34

@esafresa можешь попробовать код в это суть? Я обновлю это здесь, если это то, что вы ищете.

Dharmaraj 24.03.2022 09:49

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

https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

const [activityDataObj, setActivityDataObj] = useState({})
const userActivityIds: string[] = userData.activities

useEffect(() => {
    for (const i in userActivityIds) {
        firebase.firestore().collection("activities").doc(userActivityIds[i])
            .onSnapshot((doc) => {
                setActivityDataObj((activityDataObj) => {
                    return {...activityDataObj, [userActivityIds[i]]: doc.data()}
                })
            });
    }
}, [userActivityIds]);

Другие вопросы по теме