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

React中useState与useEffect未通过Firebase数据更新状态问题

解决React+Firestore数据拉取后map()报错问题

问题情况

部署在Firebase上的React应用,从Firestore拉取数据传给<FairytalesList>组件时,该组件调用map()方法触发错误:

react-dom.production.min.js:189 TypeError: Cannot read properties of undefined (reading 'map')

将videoData初始值设为null后,错误变为无法读取null的属性。通过异步函数getDocs()获取数据更新videoData,查询条件来自newSelectedCategory状态(由<Filters>组件的applyFilter()设置)。已确认Firestore存在目标数据,且在index.js中调用getDocs()可正常返回数据,但不想在该文件执行拉取操作。尝试将useEffect依赖项改为[videoData]无效果。

错误原因

  1. 初始状态问题:videoData初始值为undefined/null,组件初始渲染时<FairytalesList>拿到非数组值,调用map()直接报错。
  2. 依赖项缺失:useEffect未监听newSelectedCategory变化,筛选条件更新后不会重新拉取数据,状态无法同步。
  3. 查询参数错误:Firestore的where条件中,错误地将数组newSelectedCategory包裹成对象传递,导致查询失效。
  4. 数据格式错误:直接将getDocs()返回的QuerySnapshot存入videoData,该对象无法直接用map()遍历,需要转换为数组格式。

修复步骤

1. 初始化videoData为空数组

确保组件初始渲染时,<FairytalesList>拿到的是可遍历的空数组:

const [videoData, setVideoData] = useState([]);

2. 修正useEffect依赖项

让useEffect监听newSelectedCategory,筛选条件变化时自动重新拉取数据:

useEffect(() => {
  fetchVideoData();
}, [newSelectedCategory]) // 依赖改为newSelectedCategory

3. 修复Firestore查询参数

去掉where条件中包裹数组的对象,直接传递newSelectedCategory数组:

if (Array.isArray(newSelectedCategory) && newSelectedCategory.length) {
  q = query(
    collection(props.videoDatabase, "videos"), 
    where("categories", "array-contains-any", newSelectedCategory) // 移除花括号
  );
}

4. 转换QuerySnapshot为可遍历数组

将getDocs()返回的快照转换为包含文档数据和ID的数组,存入videoData:

getDocs(q)
  .then(function(snapshot) {
    // 转换快照为数组格式
    const videos = snapshot.docs.map(doc => ({
      id: doc.id,
      ...doc.data()
    }));
    setVideoData(videos);
  })

修复后完整代码示例

export function ExplorePage(props) {
  const [videoData, setVideoData] = useState([]); // 初始化为空数组
  const [alertMessage, setAlertMessage] = useState(null);
  const [isFetching, setIsFetching] = useState(false);
  const [newSelectedCategory, setNewSelectedCategory] = useState([]);

  const fetchVideoData = () => {
    setIsFetching(true);
    let q = collection(props.videoDatabase, "videos");
    if (Array.isArray(newSelectedCategory) && newSelectedCategory.length) {
      // 修复查询参数,直接传递数组
      q = query(collection(props.videoDatabase, "videos"), 
      where("categories", "array-contains-any", newSelectedCategory));
    }
    getDocs(q)
      .then(function(snapshot) {
        // 转换快照为可遍历数组
        const videos = snapshot.docs.map(doc => ({
          id: doc.id,
          ...doc.data()
        }));
        setVideoData(videos);
      })
      .catch((error) => {
        console.log(error.message);
        setAlertMessage(error.message);
      })
      .then(() => {
        setIsFetching(false);
      });
  }

  // 依赖项改为newSelectedCategory,筛选变化时重新拉取
  useEffect(() => {
    fetchVideoData();
  }, [newSelectedCategory])

  const applyFilter = (categoryArray) => {
    console.log("reached apply filter");
    setNewSelectedCategory(categoryArray)
  }

  const categoryList = ["food", "fashion", "cosmetics", "home", 
  "transportation", "children", "reduce waste", "clean energy"];

  return (
    <>
      <NavBar />
      <Filters uniqueCategory={categoryList} applyFilterCallback={applyFilter}/>
      {isFetching && <p>Loading videos...</p>}
      {alertMessage && <p className="bg-danger text-light p-3 mb-2">Failed to fetch the data. Error: {alertMessage}</p>}
      <FairytalesList categoriesQuerySnapshot={videoData}/>
      <Footer />
    </>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 03:20:01