ReactStrictMode下useSWR+Axios abortController请求被取消问题
问题分析与解决方案
问题根源
React Strict Mode在开发环境中会触发挂载-卸载-重新挂载的组件生命周期,用于检测潜在代码问题。你的自定义钩子中,AbortController在钩子顶层创建,第一次挂载生成的控制器会在Strict Mode触发的“假卸载”阶段被终止;第二次挂载时复用这个已中止的控制器,导致请求直接失败,加上配置了shouldRetryOnError: false,请求不会重新发起。
修复方案
通过useRef跟踪当前活跃的请求控制器,确保每次请求使用全新的控制器,同时保留组件真正卸载时终止请求的功能:
const useRequest = (request: AxiosRequestConfig | null): RequestType => { // 用ref跟踪当前活跃的AbortController const abortControllerRef = useRef<AbortController | null>(null); const fetcher = async (requestFetcher: AxiosRequestConfig | null): Promise<unknown> => { // 每次发起请求时创建新的控制器 const controller = new AbortController(); abortControllerRef.current = controller; try { const response = await axios({ ...requestFetcher, signal: controller.signal, }); return response; } catch (error) { // 捕获请求中止错误,避免SWR将其标记为失败请求 if (axios.isCancel(error)) { return null; } throw error; } }; const { data: dataSWR, isLoading, error } = useSWR( request && request.method === 'get' ? request.url : null, () => fetcher(request), { revalidateOnFocus: false, shouldRetryOnError: false, } ); useEffect(() => { return () => { // 组件卸载时终止当前活跃的请求 if (abortControllerRef.current) { abortControllerRef.current.abort(); abortControllerRef.current = null; } }; }, []); return { loading: isLoading, status: dataSWR ? dataSWR?.status : 0, headers: dataSWR ? dataSWR?.headers : null, data: dataSWR ? dataSWR?.data.result : null, error: error, }; };
关键修改点
- 用
useRef存储当前活跃的AbortController,每次调用fetcher时创建新控制器并更新引用,避免Strict Mode下复用已中止的控制器。 - 在
fetcher中捕获请求中止错误并返回null,防止SWR将其判定为失败请求,避免错误状态污染。 - 清理函数仅终止当前活跃的请求,Strict Mode的假卸载只会终止第一次请求,第二次请求使用新控制器可正常发起。
内容的提问来源于stack exchange,提问作者BaptisteG
相关产品推荐
相关产品推荐

