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

React Hook中自定义Hook与Reducer引发无限循环问题求助

React Hook自定义Hook结合Reducer的无限循环问题排查与解决

问题根源分析

  1. 无限循环触发原因:

    • useFetch中的两个useEffect均未将dispatch纳入依赖数组。React组件渲染时,若useEffect依赖项发生变化会重新执行回调;当CommonContext状态更新后,dispatch可能生成新引用,触发useEffect重复执行,而dispatch(setLoading)又会引发上下文状态更新,形成循环。
    • 第二个useEffect监听error状态,每次setErr后调用dispatch(setError),会再次触发上下文状态更新,加剧循环风险。
  2. 异步函数外调用setLoading(false)提前执行:
    若将dispatch(setLoading(false))放在异步函数外部,代码会在await axios.get(url)执行前就调用该方法,导致加载状态提前结束,不符合异步请求的生命周期逻辑。

解决方案

  1. 补全useEffect依赖项:将dispatch添加到所有用到它的useEffect依赖数组中,确保依赖项完整,避免不必要的重复执行。
  2. 使用AbortController处理请求取消:防止组件卸载后仍执行异步回调中的dispatch,避免内存泄漏和无效状态更新。
  3. 合并错误处理逻辑:将错误状态的dispatch直接放在catch块中,移除单独监听error的useEffect,减少不必要的状态更新触发点。

修改后的完整代码

reducer代码(无需修改)

import { SET_ERROR, SET_LOADING } from "./common.type";

export const commonInitial = {
  isLoading: false,
  isError: {
    message: "",
    status: "",
  },
};

const commonReducer = (state: any, action: any) => {
  switch (action.type) {
    case SET_LOADING:
      return { ...state, isLoading: action.payload };
    case SET_ERROR:
      return { ...state, isError: action.payload };
    default:
      return { ...state };
  }
};

export default commonReducer;

自定义Hook useFetch 修改版

import { useState, useEffect, useContext } from "react";
import axios from "axios";
import { CommonContext } from "./path-to-common-context"; // 替换为实际上下文路径
import { SET_LOADING, SET_ERROR } from "./common.type";

export const useFetch = (url: string) => {
  const [response, setResponse] = useState([]);
  const { dispatch } = useContext(CommonContext);

  useEffect(() => {
    const abortController = new AbortController();
    const signal = abortController.signal;

    const fetchDataFromUrl = async () => {
      dispatch({ type: SET_LOADING, payload: true });
      try {
        const { data } = await axios.get(url, { signal });
        setResponse(data);
        dispatch({ type: SET_LOADING, payload: false });
      } catch (err: any) {
        if (!signal.aborted) {
          dispatch({ type: SET_LOADING, payload: false });
          dispatch({
            type: SET_ERROR,
            payload: { message: err.message, status: err.response?.status || "unknown" }
          });
        }
      }
    };

    fetchDataFromUrl();

    // 组件卸载时取消请求
    return () => abortController.abort();
  }, [url, dispatch]);

  return [response];
};

Hook调用代码(无需修改)

// 使用自定义Hook获取数据
const [data] = useFetch("https://jsonplaceholder.typicode.com/posts");

return (
  <div>
    <ul>
      {!!data?.length &&
        data.map((item: Post) => {
          return (
            <div key={item.id}>
              <Link to={`/post/${item.id}`}>Moves to Post {item.id}</Link>
            </div>
          );
        })}
    </ul>
  </div>
);
};

export default DemoHookA;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 21:15:33