React fetch image from firebase storage to populate blogpost card via props

Viewed 22

hoping someone might be able to help!

I'm trying to retrieve images from firebase storage and combine these with information retrieved from firebase realtime database that combines and passes on to a blog card via props.

I've managed to retrieve all the information I need from the realtime database to populate the card and I have figured out how to access the images from the firebase storage but I cannot figure out how to link the two and pass the image into the blog via props (the name of each image file in storage is the same as the ImageID attribute of each blog to link the two).

So far I can access the images using url (and have tested this via loading it into a couple of images as seen below) but I can't figure out how to combine this into the relevant props.

Fetching blog information & firebase images:

import BlogPost from "./BlogPost";
import { useEffect, useState } from "react";

import { ref, listAll, getDownloadURL } from "firebase/storage";
import storage from "../../firebase";

interface Blog {
  id: string;
  title: string;
  desc: string;
  ImageID: string;
}

interface BlogImage {
  name: string;
  url: string;
}

const BlogsList = () => {
  const [blogs, setBlogs] = useState<Blog[]>([]);
  const [images, setImages] = useState<BlogImage[]>([]);
  const imagesRef = ref(storage, "banners/")

  useEffect(() => {
    listAll(imagesRef).then((response) => {
      response.items.forEach((item) => {
        getDownloadURL(item).then((url) => {
          setImages((prev: any) => [...prev, url])
        })
      })
    })
  }, []);

  useEffect(() => {

    const fetchBlogs = async () => {
      const response = await fetch(
        "#"
      );
      const responseData = await response.json();
      const loadedBlogs:Blog[] = [];
        
      for (const key in responseData) {
        loadedBlogs.push({
          id: key,
          title: responseData[key].title,
          desc: responseData[key].desc,
          ImageID: responseData[key].image,
        });
      }
      setBlogs(loadedBlogs);
    };
    fetchBlogs();
  });

  return (
    <div className="flexcontainer">
{images.map((url: any) => {
  return <img style={{
    width: `20%`
  }} src ={url}/>
})}

      {blogs.map((blog) => (
        <BlogPost     
          key={blog.id}
          image={blog.ImageID}
          title={blog.title}
          desc={blog.desc}
        />
      ))}
    </div>
  );
};

export default BlogsList;

Blogpost component receiving information via props

const BlogPost = (props: any) => {
  return (
    <div className={`${classes.card} flex1x5 `}>
      <header
        className={classes.card__header}
        style={{
          backgroundSize: "cover",
          background: `url(${props.image})`,
        }}
      ></header>

      <div className={classes.card__body}>
        <h3 className={classes.blog__name}>{props.title}</h3>
        <p className={classes.blog__desc}>{props.desc}</p>
        <button className={classes.arrow}>
          Read More
          <i
            className={`${classes.linkarrow} fa-sharp fa-solid fa-arrow-right`}
          ></i>
        </button>
      </div>
    </div>
  );
};

Link showing where images should load when pulled down from the server

enter image description here

Thanks for your help!

1 Answers

It'll be best if you could get all the required image URLs before the API call so you can easily pass them in your state along with the relevant data. Try the following code:

useEffect(() => {
  const fetchBlogs = async () => {
    // Fetching all image URLs
    const res = await listAll(imagesRef)
    
    const fileNames = res.items.map((itemRef) => itemRef.name)
    const promises = res.items.map((itemRef) => getDownloadURL(itemRef))

    const data = await Promise.all(promises)

    const parsedImages = fileNames.reduce((a, b, i) => {
      a[b] = data[i];
      return a
    }, {}) // { 'fileName': 'fileUrl' }

    // API request for data
    const response = await fetch(
      "#"
    );
    const responseData = await response.json();
    const loadedBlogs: Blog[] = [];

    for (const key in responseData) {
      loadedBlogs.push({
        id: key,
        title: responseData[key].title,
        desc: responseData[key].desc,
        ImageID: responseData[key].image,
        imageURL: parsedImages[responseData[key].image] // <-- use this in the components
      });
    }
    setBlogs(loadedBlogs);
  };

  fetchBlogs();
}, []);

Add a field imageURL in the Blog interface and then you use the property wherever required.

  <header
    className={classes.card__header}
    style={{
      backgroundSize: "cover",
      background: `url(${props.imageURL})`,
    }}
  ></header>
Related