使用Firebase getDocs获取子集合时遇TypeError: cannot add a new property错误
问题分析与解决方案
错误原因
- 未处理的Promise拒绝:你在
savingsSnapshot.forEach中使用了async回调函数,但forEach不会等待异步操作完成,导致这些异步任务的Promise没有被正确捕获,触发了Possible Unhandled Promise Rejection警告。 - 变量名冲突+不可扩展对象:内部
contributionsSnapshot.forEach的参数名和外部的doc重名,同时Firestore返回的doc.data()对象是不可扩展/冻结的,若误操作原对象(比如试图直接添加属性),就会抛出TypeError: cannot add a new property。
修复后的代码
let savings = []; const savingsRef = collection(userRef, "savings"); const savingsSnapshot = await getDocs(savingsRef); // 用for...of替代forEach,支持await异步操作 for (const doc of savingsSnapshot.docs) { let contributions = []; let currentSum = 0; const contributionsRef = collection(doc.ref, "contributions"); const contributionsSnapshot = await getDocs(contributionsRef); // 内部循环改用不同参数名,避免变量冲突 contributionsSnapshot.forEach((contribDoc) => { const data = contribDoc.data(); console.log(data); // 新建对象存储数据,避免操作Firestore返回的不可扩展对象 contributions.push({ ref: contribDoc.ref, ...data }); currentSum += Number(data.amount); }); // 同样新建对象存储saving数据,同时可加入计算好的总额 savings.push({ ref: doc.ref, ...doc.data(), currentSum: currentSum }); }
关键改动说明
- 替换
forEach为for...of:for...of支持在循环中使用await,确保每个异步操作完成后再执行下一次循环,彻底解决未处理Promise的问题。 - 重命名内部循环参数:将内部文档变量改为
contribDoc,避免和外部的saving文档变量doc混淆,消除潜在的误操作风险。 - 显式提取数据对象:虽然展开运算符本身不会修改原对象,但显式提取
data变量更清晰,也能彻底避免触碰Firestore返回的不可扩展对象。
内容的提问来源于stack exchange,提问作者Nicolas Woodcock
相关产品推荐
相关产品推荐

