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

如何在Axios自定义Hook中捕获请求取消错误?

问题

我正在使用Axios构建一个用于获取数据的自定义Hook。当请求在收到响应前被取消时,会产生一个无法在try/catch块中捕获的取消错误。我可以在请求后用.catch捕获它,但这样会有两处错误处理位置,还混合了ES6的then/catch与ES7的try/catch语法。请问该如何解决?

说明

  • 采用了Axios文档中推荐的AbortController方法
  • setTimeout函数仅用于调试

当前代码

const useFetch = ( dataUrl ) => {
    // Set initial state variables
    const [ data, setData ] = useState( [] );
    const [ catchError, setCatchError ] = useState( null );
    const [ isLoading, setIsLoading ] = useState( false );

    const controllerRef = useRef( false );

    // Run the effect on mount
    useEffect( () => {
        let isMounted = true;

        // Define request cancellation signal
        const controller = new AbortController();
        const { signal } = controller;

        // Fetch data function declaration
        const fetchData = async ( url ) => {
            setIsLoading( true );
            try {
                // Fetch the response
                setTimeout( async () => {
                    const response = await axios.get( url, {
                        signal
                    } )
                        // Catch cancellation error (couldn't be fetched in the catch block)
                        .catch( e => e.code === "ERR_CANCELED" && console.log( "Fetch Request Cancelled" ) );
                // Set the data
                if ( isMounted ) {
                    setData( response.data );
                    setCatchError( null );
                }
                }, 2000 )
            } catch ( err ) {
                // Catch the error
                if ( isMounted ) {
                    setCatchError( err.message );
                    setData( [] );
                }
            } finally {
                // Set loading to initial state
                isMounted && setTimeout( () => setIsLoading( false ), 2000 );
            }
        }
        // Call the Fetch Data function
        fetchData( dataUrl )

        // Cleanup the request on cancellation
        return function cleanUp() {
            console.log( 'Clean up function' );
            isMounted = false;
            controller.abort();
        };
    }, [ dataUrl ] );

    return { data, catchError, isLoading };
}

export default useFetch;

诉求

如何将下图中的取消错误捕获到try/catch块中:
Axios取消请求错误截图


解决方案

问题根源

你的代码里setTimeout包裹了await axios.get,这个setTimeout的回调是独立的异步函数,它抛出的错误无法被外层的try/catch捕获——外层try只能捕获当前函数栈的同步错误和直接await的异步错误,setTimeout的回调属于另一个执行上下文。同时,混合使用.catch和try/catch导致错误处理逻辑分散。

修正步骤

  1. 移除嵌套的异步回调:将axios.get直接放在fetchData的try块内,调试用的延迟改为await Promise形式,纳入当前异步流程。
  2. 统一用try/catch处理错误:所有错误(包括取消错误)都会进入catch块,通过判断错误码区分取消请求和其他异常。
  3. 简化状态更新逻辑:确保仅在组件挂载时执行状态更新,避免内存泄漏。

修正后的代码

import { useState, useEffect } from 'react';
import axios from 'axios';

const useFetch = (dataUrl) => {
    const [data, setData] = useState([]);
    const [catchError, setCatchError] = useState(null);
    const [isLoading, setIsLoading] = useState(false);

    useEffect(() => {
        let isMounted = true;
        const controller = new AbortController();
        const { signal } = controller;

        const fetchData = async (url) => {
            setIsLoading(true);
            try {
                // 调试用延迟,纳入当前异步流程
                await new Promise(resolve => setTimeout(resolve, 2000));
                
                const response = await axios.get(url, { signal });
                
                if (isMounted) {
                    setData(response.data);
                    setCatchError(null);
                }
            } catch (err) {
                if (isMounted) {
                    // 区分取消请求与其他错误
                    if (err.code === "ERR_CANCELED") {
                        console.log("Fetch Request Cancelled");
                        setCatchError("请求已取消");
                    } else {
                        setCatchError(err.message);
                        setData([]);
                    }
                }
            } finally {
                if (isMounted) {
                    setTimeout(() => setIsLoading(false), 2000);
                }
            }
        };

        fetchData(dataUrl);

        return function cleanUp() {
            console.log('Clean up function');
            isMounted = false;
            controller.abort();
        };
    }, [dataUrl]);

    return { data, catchError, isLoading };
};

export default useFetch;

关键说明

  • 调试用的setTimeout改为await new Promise(...)后,成为fetchData异步流程的一部分,错误能被外层try/catch捕获。
  • 取消错误会进入catch块,通过err.code === "ERR_CANCELED"判断并处理,统一了错误处理位置,不再混合两种语法。
  • 移除了无用的controllerRef,controller在useEffect内部声明,cleanup函数可直接访问。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 22:46:02