React中从Firestore获取含嵌套集合的对象格式数据问题求助
Let's break down how to solve this problem step by step, since your current attempts are hitting common Firestore structure and async pitfalls.
First, let's clarify your Firestore structure to make sure we're on the same page:
members (collection) └── JhonDoe (document, may have no stored data) └── May (subcollection) └── 28 (document) ├── expense: "1" ├── gain: "2" └── name: "Jhon Doe"
The Correct Approach
To get nested subcollections under a document and format them into your desired object structure, you need to:
- Reference the parent member document
- List all subcollections under that document (like
May) - Fetch all documents in each subcollection
- Assemble the data into the nested object format you want
Here's a working implementation using Firebase v9 (modular SDK, the current standard):
import { doc, getFirestore, listCollections, getDocs } from "firebase/firestore"; export const getMembersDailyGains = async (name) => { const db = getFirestore(); const dbName = name.replace(/\s/g, ""); // Get reference to the member's document (even if it has no data) const memberDocRef = doc(db, "members", dbName); try { const nestedData = {}; // Fetch all subcollections under the member document const subcollections = await listCollections(memberDocRef); for (const monthCollection of subcollections) { const monthName = monthCollection.id; // e.g., "May" nestedData[monthName] = {}; // Fetch all documents in this month's subcollection const dateDocs = await getDocs(monthCollection); dateDocs.forEach(dateDoc => { const dateId = dateDoc.id; // e.g., "28" nestedData[monthName][dateId] = dateDoc.data(); }); } console.log("Fetched nested data:", nestedData); return nestedData; } catch (error) { console.error("Error fetching data:", error); return {}; } };
Using This in Your React Component
Since this is an async function, you'll need to handle it properly in your component to avoid pending promises:
import { useEffect, useState } from "react"; import { getMembersDailyGains } from "./path-to-your-function"; function MemberGains() { const [gainsData, setGainsData] = useState({}); const [isLoading, setIsLoading] = useState(true); useEffect(() => { const fetchGains = async () => { const data = await getMembersDailyGains("Jhon Doe"); setGainsData(data); setIsLoading(false); }; fetchGains(); }, []); if (isLoading) return <div>Loading daily gains...</div>; return ( <div> <h3>Jhon Doe's Daily Gains</h3> <pre>{JSON.stringify(gainsData, null, 2)}</pre> </div> ); } export default MemberGains;
Why Your Previous Attempts Failed
Let's diagnose the issues in your code:
First Attempt Error:
Invalid collection referencemembers/${dbName}is a document path (2 segments:members→JhonDoe), but Firestore collection references require an odd number of segments. You were trying to treat a document as a collection, which doesn't work. You need to target the document first, then access its subcollections.
Second Attempt Error:
No such document!- Firestore allows documents to have subcollections even if the parent document itself doesn't exist (or has no stored data). Your
JhonDoedocument likely has no fields, sodoc.existsreturned false, but that doesn't mean its subcollections aren't there. You were fetching the document's data instead of its subcollections.
- Firestore allows documents to have subcollections even if the parent document itself doesn't exist (or has no stored data). Your
Pending Promise Issue:
- You mixed
awaitand.then()incorrectly, and returnedgainsListbefore the async operations finished. Using consistentawaitsyntax ensures you wait for all data to be fetched before returning the result.
- You mixed
Note on Firestore vs. Realtime Database
Firestore uses a document-collection hierarchy, unlike Realtime Database's single tree structure. This means subcollections are tied to documents, but you can't access them via a single path like you do in Realtime Database. The approach above mirrors the structure you used in Realtime Database but adapts it to Firestore's model.
内容的提问来源于stack exchange,提问作者Biki Thakur

