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

自定义useFetch钩子出现无限循环,寻求原因分析与修复方案

解决自定义useFetch钩子的无限循环问题

我来帮你拆解这个useFetch钩子无限循环的问题,其实核心原因和React的依赖比较逻辑有关,咱们一步步来看:

问题根源

你遇到的无限循环,本质是React对useEffect依赖项的浅比较机制导致的:

  • 当你在组件内直接传入init={}(取消注释后),每次组件渲染时都会创建一个全新的空对象(引用地址和上一次完全不同)。
  • 而自定义useFetch钩子的useEffect依赖数组里包含了init这个参数,React发现init的引用变了,就会重新执行useEffect里的fetch逻辑。
  • 请求完成后组件会因为数据更新重渲染,又生成新的init对象,触发新一轮的useEffect,最终形成无限循环。

两种可行的修复方案

方案1:让init保持稳定的引用(推荐,改动最小)

既然问题出在init的引用频繁变化,那我们可以用useMemo把它包裹起来,确保只有当依赖项变化时才重新创建这个对象:

import { useMemo } from 'react';

function App() {
  // 用useMemo固定init的引用,空依赖数组确保只创建一次
  const init = useMemo(() => ({
    // 如果后续需要添加请求配置(比如headers、method),直接写在这里
  }), []);

  const { data } = useFetch(
    "https://jsonplaceholder.typicode.com/todos/1",
    (res) => ({ ...res, title: res.title + " transformed" }),
    init
  );

  return <div className="App">{data?.title}</div>;
}

如果你的init需要依赖组件内的状态(比如token),只需要把依赖项加到useMemo的第二个数组里即可,比如useMemo(() => ({ headers: { Authorization: token } }), [token])。

方案2:修改useFetch钩子,支持深比较

如果你希望钩子本身能处理这种情况,不用调用方每次都处理init的引用,可以在钩子内部用useRef保存上一次的init,再通过深比较判断是否真的需要重新发起请求:

import { useState, useEffect, useRef } from 'react';
// 可以用lodash的isEqual,或者自己实现一个简单的深比较函数
import { isEqual } from 'lodash';

function useFetch(url, transform = (res) => res, init = {}) {
  const [data, setData] = useState(null);
  const prevInitRef = useRef(); // 保存上一次的init引用

  useEffect(() => {
    // 只有当init的内容真正变化时,才执行请求
    if (!isEqual(prevInitRef.current, init)) {
      const fetchData = async () => {
        try {
          const response = await fetch(url, init);
          const result = await response.json();
          setData(transform(result));
        } catch (error) {
          console.error('Fetch error:', error);
        }
      };

      fetchData();
      prevInitRef.current = init; // 更新保存的init
    }
  }, [url, transform, init]); // 依赖数组保留原参数,但内部做深比较判断

  return { data };
}

这个方案的好处是调用方不需要关心init的引用问题,钩子会自己判断是否需要重新请求。

总结

本质上这个问题是React依赖比较的特性导致的,只要解决init引用频繁变化的问题,就能终止无限循环。优先推荐方案1,因为改动小且符合React的最佳实践;如果钩子需要更健壮的兼容性,可以用方案2。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 08:47:28