You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中如何通过Props关联Firebase存储图片与博客卡片数据?

解决React中Firebase Storage图片与实时数据库博客关联的问题

我在React项目中尝试从Firebase Storage获取图片,将其与Firebase实时数据库的博客信息结合后,通过Props传递给博客卡片组件。目前已成功获取博客信息和Storage图片,但无法通过图片文件名与博客ImageID属性一致的规则将两者关联,进而把对应图片URL传给博客组件。

获取博客信息与Firebase图片的原代码

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;

接收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>
  );
};

解决方案

1. 修正图片存储逻辑,保留文件名与URL的映射

原代码仅存储图片URL,丢失了用于匹配的文件名信息。修改图片获取逻辑,将文件名和URL的对应关系存入images状态:

useEffect(() => {
  listAll(imagesRef).then((response) => {
    // 用Promise.all批量处理,减少状态更新次数
    Promise.all(
      response.items.map(item => 
        getDownloadURL(item).then(url => ({ name: item.name, url }))
      )
    ).then(imageList => {
      setImages(imageList);
    });
  })
}, []);

2. 修复博客获取的useEffect依赖,避免无限渲染

原第二个useEffect无依赖项,会导致组件无限重复请求博客数据,添加空依赖数组:

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();
}, []); // 添加空依赖数组,仅初始化时请求一次

3. 渲染时匹配对应图片URL

遍历博客列表时,通过ImageID从images数组中找到对应的图片URL,传递给BlogPost组件:

return (
  <div className="flexcontainer">
    {blogs.map((blog) => {
      // 匹配当前博客对应的图片
      const matchedImage = images.find(img => img.name === blog.ImageID);
      return (
        <BlogPost     
          key={blog.id}
          image={matchedImage?.url || ""} // 匹配到则用URL,否则留空或设置默认图
          title={blog.title}
          desc={blog.desc}
        />
      )
    })}
  </div>
);

可选优化:等待数据加载完成后再渲染

添加加载状态,避免数据未就绪时渲染空内容:

const [isLoading, setIsLoading] = useState(true);

// 并行获取图片和博客数据,减少等待时间
useEffect(() => {
  const fetchAllData = async () => {
    const [imageList, blogResponse] = await Promise.all([
      listAll(imagesRef).then(res => 
        Promise.all(res.items.map(item => 
          getDownloadURL(item).then(url => ({ name: item.name, url }))
        ))
      ),
      fetch("#").then(res => res.json())
    ]);

    setImages(imageList);
    
    const loadedBlogs:Blog[] = [];
    for (const key in blogResponse) {
      loadedBlogs.push({
        id: key,
        title: blogResponse[key].title,
        desc: blogResponse[key].desc,
        ImageID: blogResponse[key].image,
      });
    }
    setBlogs(loadedBlogs);
    setIsLoading(false);
  };

  fetchAllData();
}, []);

// 加载中状态展示
if (isLoading) {
  return <div>加载中...</div>;
}

return (
  <div className="flexcontainer">
    {/* 博客列表渲染 */}
  </div>
);

内容的提问来源于stack exchange,提问作者Olivia Bates

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 10:25:20