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

如何在React中用新版Firebase/Firestore获取集合及子集合数据?

适配新版Firebase SDK:如何获取Firestore子集合数据并关联到父文档对象

我在跟着6个月前YouTube上的Netflix克隆项目教程学习,但由于Firebase/Firestore和React推出了新版本,原教程代码无法运行。

原教程要实现的功能:

  • 访问Products集合,为每个产品文档创建对应的Product对象
  • 进入每个产品文档的Prices子集合,获取价格文档数据并添加到对应的Product对象中
  • 最终将包含价格数据的Product对象打印到控制台

我已经重构了代码以兼容新版SDK,现在能在控制台打印Product对象,但缺少Prices子集合的数据。以下是原教程代码和我的重构代码:

原教程代码

import React, { useState, useEffect } from 'react';
import db from "../firebase";
import './PlansScreen.css';

function PlansScreen() {
    const [products, setProducts] = useState([]);

    useEffect(() => {
     db.collection("products")
     .where("active", "==", true)
     .get()
     .then((querySnapshot) => {
       const products = {};
       querySnapshot.forEach(async (productDoc) => {
       products[productDoc.id] = productDoc.data();
       const priceSnap = await productDoc.ref.collection("prices").get();
       priceSnap.docs.forEach((price) => {
         products[productDoc.id].prices = {
           priceId: price.id,
           priceData: price.data(),
         };
       });
     });
     setProducts(products);
    });
 }, []);

    console.log(products)

    return (
        <div className='plansScreen'>
        </div>
    )
}

export default PlansScreen

我的重构代码

import React, { useState, useEffect } from 'react';
import db, { collection, getDocs } from "../firebase";
import './PlansScreen.css';

function PlansScreen() {
    const [products, setProducts] = useState([]);

    useEffect(() => {
        const get = async () => {
            const prodRef = collection(db, 'products');
            const prodSnap = await getDocs(prodRef);

            prodSnap.forEach(async (prodDoc) => {
                products[prodDoc.id] = prodDoc.data();
            });
            setProducts(products);
        };
        get();
    }, [products]);

    console.log(products)

    return (
        <div className='plansScreen'>
        </div>
    )
}

export default PlansScreen

我尝试了多种方法,但始终无法在创建Product对象时获取并关联子集合的数据,希望能得到适配新版SDK的解决方案。


解决方案

你的核心问题有两个:

  1. forEach不支持异步函数,导致子集合数据还没获取完就更新了状态
  2. 直接修改React state对象,且useEffect依赖错误导致潜在的无限循环

以下是修复后的完整代码:

import React, { useState, useEffect } from 'react';
import db, { collection, getDocs, query, where } from "../firebase";
import './PlansScreen.css';

function PlansScreen() {
    const [products, setProducts] = useState({}); // 改为对象存储,和原教程逻辑一致

    useEffect(() => {
        const fetchProductsWithPrices = async () => {
            // 保留原教程的active筛选条件
            const prodQuery = query(collection(db, 'products'), where("active", "==", true));
            const prodSnap = await getDocs(prodQuery);

            // 用map+Promise.all处理异步操作,确保所有子集合数据都获取完成
            const productsData = await Promise.all(
                prodSnap.docs.map(async (prodDoc) => {
                    const productData = prodDoc.data();
                    // 获取当前产品的prices子集合
                    const pricesRef = collection(prodDoc.ref, 'prices');
                    const pricesSnap = await getDocs(pricesRef);
                    
                    // 处理价格数据(这里和原教程逻辑一致,只保留最后一个价格,或可改为数组)
                    let productPrices = {};
                    pricesSnap.docs.forEach(price => {
                        productPrices = {
                            priceId: price.id,
                            priceData: price.data()
                        };
                    });

                    // 返回包含价格的产品对象,键为文档ID
                    return { [prodDoc.id]: { ...productData, prices: productPrices } };
                })
            );

            // 将所有产品对象合并为一个大对象
            const mergedProducts = productsData.reduce((acc, curr) => ({ ...acc, ...curr }), {});
            setProducts(mergedProducts);
        };

        fetchProductsWithPrices();
    }, []); // 空依赖数组,只执行一次

    console.log(products);

    return (
        <div className='plansScreen'>
        </div>
    )
}

export default PlansScreen

关键修复点说明:

  • 异步处理:用Promise.all配合map代替forEach,确保所有子集合的异步请求完成后再更新状态
  • 子集合获取:新版SDK中,通过collection(prodDoc.ref, 'prices')创建子集合引用,再用getDocs获取数据
  • 状态更新:创建新的对象存储产品数据,避免直接修改state;合并所有产品后再调用setProducts
  • 筛选条件:恢复原教程的where("active", "==", true)筛选,只获取活跃产品
  • 依赖优化:useEffect用空数组作为依赖,避免无限循环

现在运行代码,控制台打印的Product对象应该会包含对应的Prices子集合数据。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 16:45:18