Home  >  Q&A  >  body text

I'm trying to pull a list of download links from firebase storage to display in my React/nextjs component

When trying to pull the array, the data is always empty. Does anyone know where I'm going wrong? I checked the console and as far as I can tell, there is a reference to the firebase storage but no data. But there are 4 documents in my storage that should output URL links. This is my component

import React, { useState, useEffect } from 'react';

// i'm initializing firebase with firebase.initializeApp(firebaseConfig)
import { projectStorage } from '@/firebaseConfig'; 
 // projectStorage is =         firebase.storage();


const PDFViewer = () => {
  const [pdfUrls, setPdfUrls] = useState();

  useEffect(()=>{
   const storageRef = projectStorage.ref();
   const pdfRef = storageRef.child('myfirebaselocationpath/uploadeddocs/'); 
   const urls = [];
   pdfRef.listAll().then((result) => {
    result.items.forEach((itemRef) => {
    itemRef.getDownloadURL().then((url) => {
      urls.push(url);
    });
  });
  setPdfUrls(urls);
}).catch((error) => {
  console.error('Error retrieving PDF files:', error);
});
},[])
console.log(pdfUrls)
/*  console.log( pdfUrls.map((url) => {
return url
 })
) */
return (
<div>
  <h1>PDF Viewer</h1>
  {pdfUrls.map((url) => (
    <iframe key={url} src={url} width="100%" height="600px" />
  ))}
</div>
 );
};

export default PDFViewer;`

The component above is what I tried and expected a linked list to be mapped, but no data was displayed. And pdfUrls is always empty.

P粉481035232P粉481035232400 days ago726

reply all(1)I'll reply

  • P粉976488015

    P粉9764880152023-09-16 14:36:26

    The problem lies here:

    pdfRef.listAll().then((result) => {
      result.items.forEach((itemRef) => {
      itemRef.getDownloadURL().then((url) => {
        urls.push(url);
      });
    });
    setPdfUrls(urls);

    listAll and getDownloadURL are asynchronous operations and may take some time to complete. The main code does not block the entire script, but continues execution while the operation occurs - and then executes the callback block when the data (file list or download URL) is available.

    This means that your setPdfUrls(urls); runs before any urls.push(url); runs, and this sets an empty list. I recommend checking this in a debugger, or adding some logging.


    The easiest way to solve the problem is to move setPdfUrls(urls); to the internal callback:

    pdfRef.listAll().then((result) => {
      result.items.forEach((itemRef) => {
      itemRef.getDownloadURL().then((url) => {
        urls.push(url);
        setPdfUrls(urls);
      });
    });

    Now it will be called after each download URL is determined. This will perform multiple updates, so it may cause some flickering in the UI, but the data should now appear.


    Calling just once while waiting for everything to complete setPdfUrls(urls); requires more work. The easiest way is probably to mark the entire useEffect handler as async and use await in it:

    useEffect(() => async {
      const storageRef = projectStorage.ref();
      const pdfRef = storageRef.child('myfirebaselocationpath/uploadeddocs/');
      const urls = [];
      const result = await pdfRef.listAll();
      for (const itemRef of result.items) {
        const url = await itemRef.getDownloadURL();
        urls.push(url);
      }
      setPdfUrls(urls);
    }, [])

    So this code:

    • Use await to ensure that the asynchronous operation completes before calling setPdfUrls.
    • Use a for...of loop to ensure we can use await within the loop.

    reply
    0
  • Cancelreply