Приведенный ниже код работает для получения моих данных из 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]);



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Простой вызов 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) => {
// ...
})
@esafresa да, так что все onSnapshot() будут работать независимо друг от друга. Возможно, было бы лучше отправить данные из них в массив, а затем передать их в setActivityDataArray, чтобы состояние обновлялось только один раз, а не для каждого снимка. Можете ли вы попробовать сделать это, добавив массив прямо перед циклом for?
во-первых, я очень ценю вашу помощь :) не могли бы вы уточнить, что вы имеете в виду? Я думаю, мне нужно вызвать setActivityDataArray изнутри onSnapshot (т.е. onNext), иначе он не будет отображать обновления данных, верно? Я сейчас пытаюсь setActivityDataArray({...activityDataArray, ...copiedActivityObj}), то есть {...prevState, ...newObj}, но я думаю, что это не работает в реакции, объекты перезаписывают друг друга
Я понял. Теперь я понимаю, что вы имеете в виду. Благодарю вас! :D
Ах, подождите... это глючит. Я думаю, мне нужно вызвать setState внутри onSnapshot onNext, иначе он не отобразится. Предложения?
@esafresa я имел в виду, что вы можете объявить массив до начала цикла for, а затем поместить документ в этот массив. Когда у вас есть все документы, используйте setState.. можете ли вы поделиться своим обновленным кодом?
Хорошо, добавил обновленный код :)
@esafresa можешь попробовать код в это суть? Я обновлю это здесь, если это то, что вы ищете.
Я пошел с этим. Очевидно, что обновление состояния в реакции не гарантируется, поэтому правильный способ установить состояние с обновленными данными - использовать функцию обратного вызова.
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]);
Так я вроде так и делаю. У меня есть
setActivityDataArray([...activityDataArray, doc.data()])в цикле, но они перезаписывают друг друга.. т.е. рендерится только последний