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

React自定义Hook调用API无法触发catch块,如何解决?

问题

我尝试创建一个自定义Hook,希望在API调用时捕获错误以更改页面显示。以下是自定义Hook代码:

function useBook() {
    const [book, setBook] = useState<Book | any>()
    const [loading, setLoading] = useState(true)
    const [error, setError] = useState<Error | boolean>(false)

    useEffect(() => {
        const fetchData = async () => {
            try {
                const data = await getBook(Number(Object.values(id)))
                setBook(data)
                setLoading(false)
            } catch (error) {
                setError(true)
                setLoading(false)
            }
        }
        fetchData()
        dispatch({ type: 'auth/isLogin' })
    }, [])

    return { book, loading, error }
}

API函数代码如下:

export type Book = {
    title: string
    id: number
    authorName: string
    authorSurname: string
    coverImage: string
    releaseDate: string
    pages: number
    price: number
}

export type Error = {
    isError: boolean
}

async function getBook(id: number): Promise<Book | Error> {
    try {
        const { data } = await axios.get<Book>(
            `http://localhost:3000/books/${id}`,
            {
                headers: {
                    Accept: 'application/json',
                },
            }
        )
        return data
    } catch (error) {
        console.log('Error is : ', error)
        return {
            isError: true,
        }
    }
}

目前的问题是永远无法进入catch错误分支,导致error状态始终为false,请问该如何修改?


解决方案

核心问题在于getBook函数内部已经捕获了所有异常并返回了一个错误对象,而非抛出错误,导致外部的try/catch没有异常可捕获。可以通过以下两种方式修复:

方案1:让API函数抛出错误而非返回错误对象

修改getBook函数,在捕获到axios错误时直接抛出错误,这样外部的try/catch就能正常捕获:

// 重命名自定义错误类型,避免和全局Error冲突
export type ApiError = {
    isError: boolean
}

async function getBook(id: number): Promise<Book> {
    try {
        const { data } = await axios.get<Book>(
            `http://localhost:3000/books/${id}`,
            {
                headers: {
                    Accept: 'application/json',
                },
            }
        )
        return data
    } catch (error) {
        console.log('Error is : ', error)
        // 抛出错误,让外部Hook捕获
        throw error
    }
}

同时调整自定义Hook中的错误状态类型,优化类型定义与逻辑:

function useBook() {
    const [book, setBook] = useState<Book | undefined>()
    const [loading, setLoading] = useState(true)
    // 用null表示无错误,Error对象存储错误信息
    const [error, setError] = useState<Error | null>(null)

    useEffect(() => {
        const fetchData = async () => {
            try {
                const data = await getBook(Number(Object.values(id)))
                setBook(data)
                setError(null) // 请求成功时清空错误
            } catch (error) {
                setError(error as Error)
                setBook(undefined) // 请求失败时清空数据
            } finally {
                // 无论成功失败都要结束加载状态
                setLoading(false)
            }
        }
        fetchData()
        dispatch({ type: 'auth/isLogin' })
    }, [id, dispatch]) // 补充依赖项,避免闭包问题

    return { book, loading, error }
}

方案2:在Hook中手动判断返回的错误对象

如果不想修改API函数的逻辑,就在调用getBook后检查返回值是否为错误对象,手动设置错误状态:

// 先重命名自定义错误类型,避免冲突
export type ApiError = {
    isError: boolean
}

async function getBook(id: number): Promise<Book | ApiError> {
    // 原有逻辑保持不变
    try {
        const { data } = await axios.get<Book>(
            `http://localhost:3000/books/${id}`,
            {
                headers: {
                    Accept: 'application/json',
                },
            }
        )
        return data
    } catch (error) {
        console.log('Error is : ', error)
        return {
            isError: true,
        }
    }
}

然后修改自定义Hook中的逻辑:

function useBook() {
    const [book, setBook] = useState<Book | undefined>()
    const [loading, setLoading] = useState(true)
    const [error, setError] = useState(false)

    useEffect(() => {
        const fetchData = async () => {
            try {
                const data = await getBook(Number(Object.values(id)))
                if ('isError' in data && data.isError) {
                    // 识别到错误对象,设置错误状态
                    setError(true)
                    setBook(undefined)
                } else {
                    // 请求成功,设置数据并清空错误
                    setBook(data as Book)
                    setError(false)
                }
            } catch (error) {
                // 捕获getBook内部未处理的异常(比如类型转换错误)
                setError(true)
                setBook(undefined)
            } finally {
                setLoading(false)
            }
        }
        fetchData()
        dispatch({ type: 'auth/isLogin' })
    }, [id, dispatch])

    return { book, loading, error }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 05:45:43