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粉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:
await
to ensure that the asynchronous operation completes before calling setPdfUrls
. for...of
loop to ensure we can use await
within the loop.