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

使用AbortController终止Axios请求后重复请求报CanceledError问题

解决React中取消请求后再次发起触发CanceledError的问题

问题根源在于你复用了同一个AbortController实例——一旦调用abort(),该控制器的signal就会进入终止状态,后续请求再使用这个signal会直接触发取消错误。

修复步骤:

  • 每次发起请求时创建新的AbortController:确保每次请求都使用全新的可正常工作的控制器实例,避免复用已终止的实例。
  • 区分主动取消与其他错误:在异常捕获逻辑中过滤主动取消的错误,避免这类情况输出不必要的日志。

修改后的完整代码:

import { FC, useRef, useState } from 'react'
import clsx from 'clsx'

import { useAppSelector } from '../../store/hooks'

import { Button } from '@gravity-ui/uikit'
import { Modal } from '@gravity-ui/uikit'
import { Spin } from '@gravity-ui/uikit'

import avatarImg from '../../images/avatar.png'

import styles from './Profile.module.sass'
import axios, { AxiosError } from 'axios'
import { IQuote } from '../../models/UserModels'

interface ProfileProps {
    className?: string
}

export const Profile: FC<ProfileProps> = ({ className }) => {
    const [open, setOpen] = useState<boolean>(false)

    const [isLoading, setIsLoading] = useState<boolean>(false)
    const [qoute, setQoute] = useState<string>('')
    const [author, setAuthor] = useState<string>('')

    const controller = useRef<AbortController | null>(null)

    const { name } = useAppSelector((state) => state.userSlice.user)

    const handleCancelRequest = () => {
        setIsLoading(false)
        controller.current?.abort()
        setOpen(false)
    }

    const handleUpdateRequest = async () => {
        setIsLoading(true)
        setOpen(true)
        // 创建新的AbortController实例,替换旧的终止实例
        controller.current = new AbortController()

        try {
            const response = await axios.get<IQuote[]>(
                'https://api.api-ninjas.com/v1/quotes',
                {
                    headers: {
                        'X-Api-Key': 'Zhua2p7EK+yMxOiInSRp6Q==04lsXHbDKvmHe5kZ',
                    },
                    signal: controller.current.signal,
                }
            )

            setQoute(response.data[0].quote)
            setAuthor(response.data[0].author)
            setIsLoading(false)
            setOpen(false)
        } catch (error: AxiosError | unknown) {
            setIsLoading(false)
            setOpen(false)
            // 仅处理非主动取消的错误
            if (error instanceof AxiosError && error.code !== 'ERR_CANCELED') {
                console.error(error)
            }
        }
    }

    return (
        <div className={clsx(className, styles.wrapper)}>
            <div className={styles.userInfo}>
                <div className={styles.avatar}>
                    <img src={avatarImg} alt='avatar' />
                </div>
                <div className={styles.greetings}>
                    Welcome, {name || localStorage.getItem('name')}
                    <Button onClick={handleUpdateRequest} size='xl'>
                        Update
                    </Button>
                </div>
            </div>

            {author && (
                <p className={styles.subtext}>
                    <span className={styles.author}>{author}</span> -{' '}
                    <span>{qoute} ©</span>
                </p>
            )}

            <Modal open={open} onOpenChange={setOpen}>
                <div className={styles.modal}>
                    <h3 className={styles.modalTitle}>Requesting the quote</h3>

                    <div className={styles.modalContent}>
                        <span>
                            Step 1 - Requesting author...
                            {isLoading && <Spin className={styles.spinner} size='xs' />}
                        </span>
                        <span>
                            Step 2 - Requesting quote...
                            {isLoading && <Spin className={styles.spinner} size='xs' />}
                        </span>
                    </div>

                    <Button onClick={handleCancelRequest} size='m'>
                        Cancel
                    </Button>
                </div>
            </Modal>
        </div>
    )
}

关键修改说明:

  • 把controller初始值设为null,每次发起请求时生成新的AbortController实例
  • 取消请求时用可选链调用abort(),避免初始状态为null时的报错
  • 捕获异常时判断错误码,主动取消的错误不输出日志
  • 无论请求成功或失败,都重置isLoading和弹窗状态,保证UI一致性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 05:22:40