How can I add image elements to my page dynamically?

101 Views Asked by At

My React app is connected to Firebase Firestore and Storage, and I'm having trouble with reading the files from Storage and displaying them.

At one point, I have the following array of data:

metricData = [

  "2021-07-15" = { id1: "/image1url", id2: "/image2url", ... },
  "2021-07-14" = { id2: "/image2url", ... },
  ...

]

where 'id1' etc are the firebase Ids for various metrics, and the values of each (eg '/image1.url') are the paths to images which are stored in my Firebase Storage.

Then I have an array of the ids I wish to view:

metricIds = [ id1, id2, ..., idx ]

So, I want to loop over metricIds, and for each id in there, I want to look at the record in metricData for today (2021-07-15), and get the url associated with that id. Then I want to read in the associated image, and display it.

I'm having trouble with rendering the images because by the time I get them from Firebase, React has moved on and doesn't render them.

So far I have the following (I've taken out all the error checking, for clarity):

const MetricImages = (metricIds, metricData) => {

  const firebase = useContext(FirebaseContext)
  const today = new Date()

  return (
    <>
      {
        metricIds.map((metricId) => {
          // Get image url
          const url = metricData[moment(today).format('YYYY-MM-DD')][metricId]
          // Now get image at that url from Firebase storage
          firebase
          .doGetFile(url)
          .then((resImage:string) => {
            // resImage should now contain the relevant image, so we want to display it
          })
        })
      }
    </>
            

export default MetricImages

The question is, where do I put the return for the map statement, and return an image element? I want to return something like

<img key={metricId} src={resImage} />

for each metricId, so the final Component should produce an element like:

  <>
   <img key='id1' src='image1src' />
   <img key='id2' src='image2src' />
   ... more images ...
  </>

If I do return within the 'then' statement, React just carries on and never picks it up. I tried calling firebase.doGetFile() with await (changed the map function to async), but that still gave me nothing.

If I put the return after the 'then', I don't have the src value at that point, so I just return <img key='id1' src='' /> which obviously doesn't work.

What can I try next?

1

There are 1 best solutions below

1
On BEST ANSWER

You should store them into useState then fetch your data in component did mount cycle which is useEffect in functional components and then when Firebase gives your images in the then, set them into the variable that you will keep with useState. I am not familiar the Firebase flow but you should do something like this:

const MetricImages = (metricIds, metricData) => {
   const [images, setImages] = useState([]);

   useEffect(() => {
      // Your data fetch methods will be here
      const url = metricData[moment(today).format('YYYY-MM-DD')][metricId]
      firebase
          .doGetFile(url)
          .then((resImage) => {
            setImages(resImage)
          })
   }, [])


   return <>
    {images.length > 0 ?
      {images.map(image =>
         <img src={image.src} ... />
      )}
    : <>loading..</>}
   </>
}