Возвращенный массив, оцененный позже из асинхронных вызовов Firestore, не обновляется в React

# #javascript #reactjs #firebase #google-cloud-firestore

Вопрос:

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

 useEffect(() =gt; {  async function getAllImages() {  const images = await imageStorage.downloadAllImages(true); // images is first empty and then populated after the html has been rendered  setImages(images);  }    getAllImages();    }, []);  

отображение изображений:

 const handleGetImages = (images) =gt; {  console.log("images", images); // image is first empty and then populated after the html has been rendered  return images.map((imageData) =gt; {  return (  lt;Grid.Column key={imageData.url}gt;  lt;span class="image left"gt;  lt;img src={imageData.url} style={styles.image} alt="" /gt;  lt;/spangt;  lt;/Grid.Columngt;  );  });  };  

imageStorage.js

 export async function downloadAllImages(thumbnail) {  const imageRef = collection(db, "images");  console.log("hi");  var images = [];   const q = query(imageRef, orderBy("date", "desc"), limit(10));  const querySnapshot = await getDocs(q);   querySnapshot.forEach(async (doc) =gt; {  var imagePath = doc.data().ref; // image/task/...  if (thumbnail) imagePath = imagePath.replace("images", "thumbnails");  // GET metadata of the image  const imageRef = ref(storage, imagePath);   // GET [imagePrefix] URL  const url = await getDownloadURL(imageRef);  const fullMetadata = await getMetadata(imageRef);   images.push({  path: imagePath,  url: url,  metadata: fullMetadata.customMetadata,  });  });   return images; }  

Есть какой-нибудь способ это исправить? Спасибо!

Ответ №1:

Ваш код близок. Array.prototype.forEach однако это синхронно. Я подозреваю, что ваша downloadAllImages функция не ждет завершения обратных вызовов цикла и возвращает images массив, который еще не заполнен.

Переработайте логику, чтобы создать массив обещаний (функции async обратного вызова) и дождаться Promise.all их, тогда у вас будет заполненный массив изображений для возврата.

 export async function downloadAllImages(thumbnail) {  const imageRef = collection(db, "images");   const q = query(imageRef, orderBy("date", "desc"), limit(10));  const querySnapshot = await getDocs(q);   // Map querySnapshot docs to array of async functions (Promises)  const imageRequests = querySnapshot.docs.map(async (doc) =gt; {  const imagePath = doc.data().ref; // image/task/...  if (thumbnail) imagePath = imagePath.replace("images", "thumbnails");  // GET metadata of the image  const imageRef = ref(storage, imagePath);   // GET [imagePrefix] URL  const url = await getDownloadURL(imageRef);  const fullMetadata = await getMetadata(imageRef);   // Return image objects  return {  path: imagePath,  url: url,  metadata: fullMetadata.customMetadata,  };  });   // Return array of resolved promises (i.e. the image objects)  return Promise.all(imageRequests); }  

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

1. Большое вам спасибо! Я потратил на это так много времени, ха-ха, это сработало идеально! Только одно, там написано, что querySnapshot.map-это не функция, думаю, она должна querySnapshot.docs.map(...) быть.

2. @guckmalmensch Спасибо, обновил свой ответ, чтобы отразить это.