React Strict Mode导致自定义Fetch Hook请求中止问题
解决React StrictMode下useFetch Hook请求被中止的问题
在启用React StrictMode时,自定义的useFetch Hook会出现请求被AbortSignal中止的问题,表现为response.loading始终无法保持为true,日志中会出现请求中止的错误;但移除StrictMode后,Hook可以正常工作。
问题原因
React StrictMode在开发环境中会重复执行组件的生命周期函数和Hook(包括useEffect的回调与清理函数),以此检测潜在的代码问题。具体到这个场景:
- 第一次执行useEffect发起请求后,StrictMode会立即调用清理函数中止该请求;
- 请求的catch块捕获到AbortError,触发error动作将loading设为false;
- 第二次执行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
相关产品推荐
相关产品推荐

