React结合Firebase 9.x使用useEffect获取Firestore数据时的双重错误问题求助
destroy is not a function、forEach is not a function错误 问题根源分析
咱们一步步拆解你遇到的两个核心问题:
Uncaught TypeError: destroy is not a function:
这个错误是因为你直接把async函数作为useEffect的回调了。useEffect的回调要么返回一个清理函数(用于组件卸载/依赖更新时清理),要么返回undefined。而async函数默认返回Promise对象,React会把这个Promise当成清理函数去执行,自然就会报错“destroy is not a function”。Uncaught TypeError: querySnapshot.forEach is not a function:
当你移除async/await后,getDocs是异步操作,直接调用会返回一个Promise,而不是Firestore的QuerySnapshot实例——Promise上没有forEach方法,所以报错。
分步解决方案
1. 修正useEffect的异步函数使用方式
不要直接把async作为useEffect的回调,而是在useEffect内部定义异步函数并调用:
useEffect(() => { // 内部创建异步函数 const fetchSellerDetails = async () => { // 先判断postDetails是否存在,避免userId为undefined if (!postDetails?.userId) return; const usersRef = collection(firebase.firestore(), "users"); const q = query(usersRef, where("id", "==", postDetails.userId)); const querySnapshot = await getDocs(q); querySnapshot.forEach(doc => { console.log("Seller data:", doc.data()); setUserDetails(doc.data()); }); }; fetchSellerDetails(); // 调用异步函数 }, [postDetails, firebase]); // 添加依赖项
2. 修复依赖数组
之前你的依赖数组是空的[],但postDetails是从上下文获取的异步数据——如果页面初始化时postDetails还没加载完成,userId会是undefined,导致查询失败。把postDetails和firebase加入依赖数组,确保数据更新时重新执行查询。
3. 优化状态初始化与渲染安全
你初始化userDetails为空字符串"",第一次渲染时访问userDetails.username会报错(字符串没有username属性),这也是页面白屏的原因之一:
- 把初始值改为
null或空对象:const [userDetails, setUserDetails] = useState(null); - 渲染时用**可选链操作符
?.**安全访问属性,同时添加加载提示:<div className="contactDetails"> <p>Seller details</p> <p>{userDetails?.username || "Loading seller info..."}</p> <p>{userDetails?.phone || "Loading seller info..."}</p> </div>
4. 处理postDetails未加载的情况
如果postDetails是异步获取的,页面初始化时可能为undefined,直接渲染会导致postDetails.url等属性报错,所以添加加载状态:
if (!postDetails) { return <div className="viewParentDiv">Loading product details...</div>; }
完整修改后的代码
import React, { useContext, useEffect, useState } from 'react'; import { FirebaseContext } from '../../store/Context'; import { collection, query, where, getDocs } from "firebase/firestore"; import './View.css'; import { PostContext } from '../../store/postContext'; function View() { const [userDetails, setUserDetails] = useState(null); const { postDetails } = useContext(PostContext); const { firebase } = useContext(FirebaseContext); useEffect(() => { const fetchSellerDetails = async () => { if (!postDetails?.userId) return; const usersRef = collection(firebase.firestore(), "users"); const q = query(usersRef, where("id", "==", postDetails.userId)); const querySnapshot = await getDocs(q); querySnapshot.forEach(doc => { setUserDetails(doc.data()); }); }; fetchSellerDetails(); }, [postDetails, firebase]); // 处理产品详情未加载的情况 if (!postDetails) { return <div className="viewParentDiv">Loading product details...</div>; } return ( <div className="viewParentDiv"> <div className="imageShowDiv"> <img src={postDetails.url} alt={postDetails.name} /> </div> <div className="rightSection"> <div className="productDetails"> <p>₹ {postDetails.price}</p> <span>{postDetails.name}</span> <p>{postDetails.category}</p> <span>{postDetails.createdAt}</span> </div> <div className="contactDetails"> <p>Seller details</p> <p>{userDetails?.username || "Loading seller info..."}</p> <p>{userDetails?.phone || "Loading seller info..."}</p> </div> </div> </div> ); } export default View;
额外提示
- 你导入的
'firebase/compat/firestore'可以删掉,因为已经用了Firebase 9的模块化API,不需要兼容模式。 - 确保Firestore中
users集合的id字段和postDetails.userId完全匹配(比如都是字符串类型,值一致),否则查询会返回空结果。
内容的提问来源于stack exchange,提问作者Sojo C Johny

