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

React Strict Mode导致自定义Fetch Hook请求中止问题

解决React StrictMode下useFetch Hook请求被中止的问题

在启用React StrictMode时,自定义的useFetch Hook会出现请求被AbortSignal中止的问题,表现为response.loading始终无法保持为true,日志中会出现请求中止的错误;但移除StrictMode后,Hook可以正常工作。

问题原因

React StrictMode在开发环境中会重复执行组件的生命周期函数和Hook(包括useEffect的回调与清理函数),以此检测潜在的代码问题。具体到这个场景:

  1. 第一次执行useEffect发起请求后,StrictMode会立即调用清理函数中止该请求;
  2. 请求的catch块捕获到AbortError,触发error动作将loading设为false;
  3. 第二次执行useEffect再次发起请求,但之前错误状态的干扰会导致loading无法正常展示。

修复方案

在catch块中判断错误是否为AbortError,若是则不处理该错误(这是StrictMode下的预期清理行为,不属于业务错误),避免错误状态被错误触发。

修改后的useFetch Hook代码:

import { useEffect, useReducer } from 'react';
import { ApiResponse } from '../interfaces/ApiResponse';

const initialState: ApiResponse = {
  loading: false,
  data: null,
  error: null,
};

type Action =
  | { type: 'start' }
  | { type: 'error'; payload: Error }
  | { type: 'success'; payload: JSON };

const reducer = (state: ApiResponse, action: Action) => {
  switch (action.type) {
    case 'start':
      return {
        loading: true,
        data: null,
        error: null,
      };
    case 'success':
      return {
        loading: false,
        data: action.payload,
        error: null,
      };
    case 'error':
      return {
        loading: false,
        data: null,
        error: action.payload,
      };

    default:
      return state;
  }
};

export const useFetch = (url: string): ApiResponse => {
  const [response, dispatch] = useReducer(reducer, initialState);
  useEffect(() => {
    const controller = new AbortController();
    const signal = controller.signal;
    const fetchData = async () => {
      dispatch({ type: 'start' });
      try {
        const res = await fetch(url, { signal });
        if (res.ok) {
          const json = await res.json();
          dispatch({
            type: 'success',
            payload: json,
          });
        } else {
          dispatch({
            type: 'error',
            payload: new Error(res.statusText),
          });
        }
      } catch (error: any) {
        // 忽略StrictMode下的请求中止错误
        if (error.name !== 'AbortError') {
          dispatch({
            type: 'error',
            payload: new Error(error.message || '请求失败'),
          });
        }
      }
    };
    fetchData();
    return () => {
      controller.abort();
    };
  }, [url]);
  return response;
};

补充说明

  • StrictMode的双重执行仅发生在开发环境,生产环境不会有该行为,因此这个修复不会影响生产环境的正常逻辑;
  • 通过过滤AbortError,第二次发起的请求可以正常触发start动作,将loading设为true,直到请求完成或出现真正的业务错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 01:31:35