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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 22:45:33