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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 16:15:42