Firestore读取不存在文档触发未捕获Promise及组件重复渲染问题排查
错误排查与修复方案
1. db.ref is not a function 错误
这个错误的核心是API混用:你当前导出的db是Firestore实例(通过getFirestore()创建),而ref()是Firebase Realtime Database的专属方法,两者属于不同的Firebase服务。
- 检查所有导入
db的模块(包括你用到的Form组件),确认没有对Firestore实例调用ref()。 - 如果确实需要操作Realtime Database,在
configs.js中单独初始化并导出对应的实例:
后续用import { getDatabase } from "firebase/database"; // 放在Firestore初始化代码之后 export const realtimeDb = getDatabase(app);realtimeDb.ref()操作实时数据库,Firestore继续使用db配合doc()/collection()等方法。
2. Cannot read properties of undefined (reading 'ex') 错误
该错误源于异步操作中访问未定义的属性,结合你的代码,主要出现在两个场景:
场景1:Firestore文档不存在
首次登录的用户,users集合中可能没有对应UID的文档,此时data.data()会返回undefined,读取ingredients时直接报错。修复getUsers函数:
const getUsers = async () => { const data = await getDoc(docRef); if (data.exists()) { // 兜底空数组,避免后续渲染报错 setImgArr(data.data().ingredients || []); } else { // 首次登录时初始化用户文档 await setDoc(docRef, { ingredients: [] }); setImgArr([]); } };
场景2:Edamam API返回数据异常
直接访问data.parsed[0].food会忽略API返回无匹配结果的情况,导致读取属性时报错。给getResponse添加异常判断:
async function getResponse() { const params = new URLSearchParams({ q: form }); const response = await fetch( `https://edamam-food-and-grocery-database.p.rapidapi.com/parser?ingr=${params}`, { method: "GET" } ); const data = await response.json(); // 校验API返回数据结构 if (!data.parsed || data.parsed.length === 0) { throw new Error("未找到匹配的食材"); } const food = data.parsed[0].food; return { label: food.label, image: food.image, id: Math.random(), }; }
3. 组件多次渲染问题
组件重复渲染主要由三个原因导致,逐一修复:
原因1:onAuthStateChanged未取消订阅
每次useEffect触发都会添加新的监听,导致回调重复执行。添加清理函数取消订阅:
useEffect(() => { // 保存订阅的卸载函数 const unsubscribe = onAuthStateChanged(auth, (user) => { if (user) { // 直接使用参数user的uid,避免依赖auth.currentUser const uid = user.uid; const docRef = doc(db, "users", uid); const getUsers = async () => { const data = await getDoc(docRef); if (data.exists()) { setImgArr(data.data().ingredients || []); } else { await setDoc(docRef, { ingredients: [] }); setImgArr([]); } }; getUsers(); } else { setImgArr([]); } }); // 组件卸载时取消订阅 return () => unsubscribe(); }, [auth]);
原因2:重复创建auth实例
组件内调用getAuth()会导致useEffect依赖不稳定,直接导入configs.js中导出的auth即可:
// 替换组件内的getAuth()导入 import { auth } from "../configs"; // 删除组件内的const auth = getAuth();
原因3:不必要的状态更新
clickHandler中调用setForm(e.target.value)属于重复更新,handleChange已经在输入时同步了form状态,直接删除该行即可。
额外优化建议
- 使用函数式更新确保状态最新:
clickHandler中setImgArr([...imgArr, newImg])改为setImgArr(prev => [...prev, newImg]),避免依赖旧状态。 - 删除
handleChange中的e.preventDefault():输入框的onChange事件不需要阻止默认行为。
内容的提问来源于stack exchange,提问作者chickenwings123
相关产品推荐
相关产品推荐

