#javascript #reactjs #react-native #google-cloud-firestore #react-redux
#javascript #reactjs #react-native #google-облако-firestore #react-redux
Вопрос:
У меня есть список ссылок на документы Firebase, и мне нужно получить данные и установить состояние с этими данными.
Это то, что я пробовал:
useEffect(() => {
let x = [];
roomData.players.forEach(
async (playerRef: FirebaseFirestoreTypes.DocumentReference) => {
const playerSnap = await playerRef.get()
x.push(playerSnap.data())
}
);
setPlayers(x)
Список состоит из 2 элементов.
Состояние устанавливается 3 раза. Обычно один раз с двумя объектами. Во второй раз значение равно пустому массиву и 3 только с одним объектом.
Ответ №1:
Во-первых, вы пропустили критическую часть, которая является вторым параметром useEffect, который определяет, когда вызываются setPlayers.
Во-вторых, асинхронный код, вероятно, выполняется не так, как вы ожидаете, поскольку содержимое x, когда оно передается установщикам, не синхронизировано. Вы не можете использовать async в forEach, поскольку нет способа дождаться всех результатов. Таким образом, в этом случае setPlayers(x)
может быть вызван раньше x.push(playerSnap.data())
.
Итак, самое простое изменение — переписать первый параметр useEffect как
roomData.players.forEach(
async (playerRef: FirebaseFirestoreTypes.DocumentReference) => {
const playerSnap = await playerRef.get();
setPlayers(prevPlayers => [...prevPlayers, playerSnap]);
}
);
Это приводит к асинхронному обновлению проигрывателей.
Если вам нужно, чтобы проигрыватели обновлялись синхронно, например, чтобы обновить пользовательский интерфейс только один раз или отразить порядок проигрывателей, как в Firestore, forEach необходимо заменить. Одним из таких способов является использование простого цикла for для последовательного получения. Другой способ — создать массив promise и передать его в Promise.all
.