React+Firebase CRUD应用useEffect无限调用致Firebase读取额度耗尽
问题分析与解决方案
一、无限调用的核心原因
当前代码里useEffect用了空依赖数组,理论上仅在组件挂载时执行一次,但出现无限调用大概率是你希望数据更新时自动重新获取,但现有逻辑没做正确控制,加上可能组件因状态反复更新触发重复渲染,最终导致getData被循环调用。
二、针对性解决方案
1. 先终止无限调用,确保初始数据仅加载一次
用useCallback包裹getData函数,避免组件每次渲染都重新创建该函数,同时修正useEffect的依赖项,确保仅在必要时执行:
import { useCallback, useEffect, useState } from 'react'; // ...其他代码 const getData = useCallback(async () => { console.log("no. of getData calls"); const docRef = collection(db, "bunkManager", `${userEmail}`, "course"); const dataDB = await getDocs(docRef); const allData = dataDB.docs.map(val => ({course:val.id, ...val.data()})); setRows(allData); }, [db, userEmail]); // 仅当db或userEmail变化时,重新创建getData函数 useEffect(() => { getData(); console.log("useEffect"); }, [getData]); // 依赖getData,确保仅在getData更新时执行
2. 操作后手动刷新数据
满足你“表格更新时获取数据”的需求,在添加、删除操作完成后主动调用getData:
const handleSubmit = async (newRow) => { await setDoc(doc(db,"bunkManager", userEmail, "course",newRow.course), { bunkedHours: newRow.bunkedHours, totalHours: newRow.totalHours }); getData(); // 添加完成后刷新数据 }; const handleDeleteRow = async (course) => { await deleteDoc(doc(db, "bunkManager", userEmail, "course",course)); getData(); // 删除完成后刷新数据 };
3. 更优方案:用Firebase实时监听自动同步
如果希望数据实时同步,无需手动调用刷新,可以用Firebase的onSnapshot监听集合变化,数据更新时自动触发状态更新:
useEffect(() => { const docRef = collection(db, "bunkManager", `${userEmail}`, "course"); // 监听集合数据变化 const unsubscribe = onSnapshot(docRef, (querySnapshot) => { const allData = querySnapshot.docs.map(val => ({course:val.id, ...val.data()})); setRows(allData); console.log("数据已同步更新"); }); // 组件卸载时取消监听,避免内存泄漏 return () => unsubscribe(); }, [db, userEmail]);
这种方式既不会出现无限调用问题,还能实时同步Firebase的最新数据,省去手动刷新的步骤。
三、额外注意点
- 确保
userEmail值稳定,若localStorage.getItem("email")可能在组件渲染过程中变化,可单独加useEffect监听userEmail变化并重新同步数据 - 开发环境下React严格模式会让
useEffect执行两次,若要验证是否是这个原因,可暂时移除index.js中的<React.StrictMode>标签
内容的提问来源于stack exchange,提问作者Mahesh Paul
相关产品推荐
相关产品推荐

