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

为什么自定义Hook搭配Context、useEffect会触发渲染与Firebase请求无限循环?

问题根因分析

导致无限循环+无限请求Firebase的问题一共两个核心触发点:

1. useProjects自定义Hook的useEffect依赖逻辑错误

你把projects作为useEffect的依赖项,直接形成了闭环逻辑:

  • 每次projects状态变化都会触发useEffect重新执行
  • 执行后发起Firebase请求,请求返回后你用JSON.stringify对比两个数组的判断本身不可靠,只要对比判定值不同,就会触发setProjects更新projects状态
  • 状态更新后又再次触发useEffect执行,形成请求→更新状态→再请求→再更新的无限循环

额外提醒:用JSON.stringify对比对象数组是不可靠的,若两个对象属性顺序不同、存在undefined/function等不可序列化值时,会出现内容相同但判定为不等的情况,也会额外触发状态更新

2. Projects组件列表项key值使用Math.random()加剧重渲染

列表的key属性是React用来判断列表项是否需要复用更新的核心标识:

  • 你每次渲染都给每个li生成随机的key,等于每次渲染React都认为所有列表项是全新的元素,会销毁之前的所有列表项重新渲染
  • 结合Context的状态传递机制,每次重渲染都会触发useProjects的重新调用,进一步放大了无限循环的问题

修复方案

修复后的useProjects Hook

把useEffect的依赖项改为空数组[],仅在Hook第一次挂载时发起请求即可,如果需要监听数据实时更新,可以替换为Firebase的onSnapshot监听方法,不要把projects作为请求触发的依赖。

export const useProjects = () => {
  const [projects, setProjects] = useState([]);
  useEffect(() => {
    const getProjects = async () => {
      try {
        const result = await firebase
          .firestore()
          .collection("projects")
          .where("userId", "==", "1234567890")
          .orderBy("projectId")
          .get();
        const allProjects = result.docs.map((project) => ({
          docId: project.id,
          ...project.data(),
        }));
        setProjects(allProjects);
      } catch (err) {
        console.log("useProjects error", err);
      }
    }
    getProjects();
  }, []);
  return { projects, setProjects };
};

修复后的Projects组件

把列表项的key替换为后端返回的稳定唯一ID,不要用随机值:

const Projects = () => {
  const [active, setActive] = useState(null);
  const { setSelectedProject } = useSelectedProjectValue();
  const { projects } = useProjectsValue();

  return (
    projects &&
    projects.map((project) => (
      <li
        key={project.docId}
        className={
          active === project.projectId
            ? "sidebar__project active"
            : "sidebar__project"
        }
      >
        <div
          onClick={() => {
            setActive(project.projectId);
            setSelectedProject(project.projectId);
          }}
        >
          <IndividualProject
            className="individualProjectComp"
            project={project}
          />
        </div>
      </li>
    ))
  );
};

内容的提问来源于stack exchange,提问作者Mahmoud

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.11 04:15:03