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

React项目中axios CancelToken失效问题排查求助

解决React中axios CancelToken无效的问题

你的代码存在两个核心问题,导致CancelToken无法正常工作:

  1. 普通变量无法跨渲染保存状态:函数组件每次执行时,let axiosCancelToken都会被重新声明并初始化为undefined。当用户连续输入触发多次getData时,后续调用无法获取上一次的CancelToken实例,自然无法取消未完成的旧请求。
  2. 逻辑顺序错误:你在尝试取消旧请求后立刻创建新的CancelToken,但此时旧请求并未关联到这个新令牌,正确流程应该是先取消旧请求,再创建新令牌并发新请求。

修正后的基础版本代码

import React, { useRef } from 'react';
import axios from 'axios';

export default function Home() {
    // 用useRef保存CancelToken引用,避免组件渲染时重置
    const axiosCancelTokenRef = useRef(null);

    async function getData() {
        // 取消上一次未完成的请求
        if (axiosCancelTokenRef.current) {
            axiosCancelTokenRef.current.cancel('用户输入新内容,取消旧请求');
            console.warn('request cancelled');
        }

        // 创建新的CancelToken
        axiosCancelTokenRef.current = axios.CancelToken.source();

        try {
            const response = await axios({
                method: 'GET',
                url: 'https://jsonplaceholder.typicode.com/todos/1',
                cancelToken: axiosCancelTokenRef.current.token
            });
            // 处理请求结果
            console.log('请求成功', response.data);
        } catch (error) {
            // 捕获取消请求的错误,避免控制台报错
            if (axios.isCancel(error)) {
                console.log('请求已取消:', error.message);
            } else {
                // 处理其他网络错误
                console.error('请求失败', error);
            }
        }
    }

    return (
        <input type="text" onChange={getData} />
    );
}

结合防抖的优化版本

既然你希望仅在输入结束后发送请求,直接取消请求仍会产生多次无效的取消操作,加入防抖逻辑能进一步减少API调用次数:

import React, { useRef, useCallback } from 'react';
import axios from 'axios';

export default function Home() {
    const axiosCancelTokenRef = useRef(null);
    // 保存防抖定时器的引用
    const debounceTimerRef = useRef(null);

    const getData = useCallback(async () => {
        // 清除上一次的防抖定时器
        if (debounceTimerRef.current) {
            clearTimeout(debounceTimerRef.current);
        }

        // 取消上一次未完成的请求
        if (axiosCancelTokenRef.current) {
            axiosCancelTokenRef.current.cancel('用户输入新内容,取消旧请求');
            console.warn('request cancelled');
        }

        // 设置300ms防抖延迟,用户停止输入后再发起请求
        debounceTimerRef.current = setTimeout(async () => {
            axiosCancelTokenRef.current = axios.CancelToken.source();
            try {
                const response = await axios({
                    method: 'GET',
                    url: 'https://jsonplaceholder.typicode.com/todos/1',
                    cancelToken: axiosCancelTokenRef.current.token
                });
                console.log('请求成功', response.data);
            } catch (error) {
                if (axios.isCancel(error)) {
                    console.log('请求已取消:', error.message);
                } else {
                    console.error('请求失败', error);
                }
            } finally {
                // 请求完成或取消后清空定时器引用
                debounceTimerRef.current = null;
            }
        }, 300);
    }, []);

    return (
        <input type="text" onChange={getData} />
    );
}

关键优化点说明

  • 使用useRef存储CancelToken和防抖定时器,确保它们在组件整个生命周期内保持引用,不会因组件渲染被重置。
  • 防抖逻辑设置延迟时间(示例为300ms),仅当用户停止输入超过该时间时才发起请求,大幅减少不必要的API调用。
  • 通过try/catch捕获请求错误,区分取消请求的错误与其他网络错误,避免控制台出现冗余报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 04:30:52