React项目中axios CancelToken失效问题排查求助
解决React中axios CancelToken无效的问题
你的代码存在两个核心问题,导致CancelToken无法正常工作:
- 普通变量无法跨渲染保存状态:函数组件每次执行时,
let axiosCancelToken都会被重新声明并初始化为undefined。当用户连续输入触发多次getData时,后续调用无法获取上一次的CancelToken实例,自然无法取消未完成的旧请求。 - 逻辑顺序错误:你在尝试取消旧请求后立刻创建新的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
相关产品推荐
相关产品推荐

