如何在useEffect中正确调用函数?解决页面加载超千次读取问题
解决useEffect仅在页面加载时正确调用函数并避免过量读取的问题
核心问题分析
- useEffect依赖缺失:你的
useEffect使用空依赖数组,但未监听user和loading状态。useAuthState是异步的,初始渲染时user可能为undefined,此时执行查询会发起无效请求(查询uid为undefined的文档);且user加载完成后,useEffect不会重新触发,导致无法获取正确的用户数据。 fetchTotalProjects逻辑错误:混用await与.then(),后续访问query.docs[0].data()会抛出错误(query为.then()的无返回值结果,即undefined),同时两次调用setProjectList会引发不必要的组件渲染。- 无重复请求防护:未标记数据是否已加载,可能因组件重复渲染触发多次无效请求,耗尽配额。
修复后的代码
import { useState, useEffect, useCallback } from 'react'; import { useAuthState } from 'react-firebase-hooks/auth'; import { db } from './your-firebase-config'; // 替换为你的Firebase配置路径 const YourComponent = () => { const [user, loading] = useAuthState(auth); const [myDisplayName, setMyDisplayName] = useState(""); const [projectList, setProjectList] = useState([]); const [isDataLoaded, setIsDataLoaded] = useState(false); // 标记数据是否已加载完成 // 用useCallback包裹,避免组件渲染时重复创建函数 const fetchUserName = useCallback(async () => { if (!user?.uid) return; // 确保user存在后再执行查询 try { const querySnapshot = await db .collection("user") .where("uid", "==", user.uid) .get(); if (!querySnapshot.empty) { const data = querySnapshot.docs[0].data(); setMyDisplayName(data.firstName); } } catch (err) { console.error("获取用户名失败:", err); } }, [user]); const fetchTotalProjects = useCallback(async () => { if (!user?.uid) return; try { const querySnapshot = await db .collection("projects") .where("uid", "==", user.uid) .get(); const tempData = []; querySnapshot.forEach((doc) => { tempData.push(doc.data()); }); setProjectList(tempData); } catch (err) { console.error("获取项目列表失败:", err); } }, [user]); useEffect(() => { // 仅在loading完成、user存在且数据未加载过时执行请求 if (!loading && user && !isDataLoaded) { const fetchData = async () => { // 并行执行两个请求,提升加载效率 await Promise.all([fetchUserName(), fetchTotalProjects()]); setIsDataLoaded(true); // 标记数据已加载,防止重复请求 }; fetchData(); } else if (!loading && !user) { navigate("/login"); } }, [loading, user, isDataLoaded, fetchUserName, fetchTotalProjects, navigate]); return ( <div> {loading ? <p>加载中...</p> : ( <> <p>当前用户: {myDisplayName}</p> <h3>我的项目</h3> <ul> {projectList.map((project, idx) => ( <li key={idx}>{project.name}</li> {/* 替换为你的项目字段 */} ))} </ul> </> )} </div> ); }; export default YourComponent;
关键修复说明
- 完善依赖监听:
useEffect添加loading、user、isDataLoaded等依赖,确保在正确时机触发请求,避免无效执行。 - 函数缓存优化:用
useCallback包裹查询函数,避免组件重复渲染时重新创建函数,同时保证函数能获取最新的user状态。 - 无效请求拦截:查询函数开头判断
user?.uid是否存在,避免发起无意义的查询;用isDataLoaded标记加载状态,彻底防止重复请求。 - 逻辑简化修复:移除
fetchTotalProjects中混用的await与.then(),修复错误逻辑,避免不必要的状态更新。 - 请求效率提升:用
Promise.all并行执行两个异步请求,减少整体加载时间。
内容的提问来源于stack exchange,提问作者Tom Galland
相关产品推荐
相关产品推荐

