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

React/TypeScript栈下useEffect异步任务清理问题咨询(含React Query/Axios)

问题根因

这个报错的触发条件是组件已经卸载后,仍执行了状态更新操作,你的代码里存在两个核心问题:

  • 你在getAlertsFunction的请求回调里直接调用了setFetchData、setDisplayData,如果请求还没返回时组件就卸载,回调执行时就会触发警告
  • 当前useEffect的清理逻辑完全无效,你只是新建了一个空的CancelToken实例,没有关联到正在发送的请求,也没有执行取消操作
最优解决方案(推荐)

React Query本身已经封装了请求结果的存储、缓存、卸载逻辑,你完全不需要自定义useState存储请求返回数据,直接用useQuery返回的data字段即可,修改后代码如下:

const queryKey = 'planes';

const getAlertsFunction = async (): Promise<IData[]> => {
  await new Promise(resolve => setTimeout(resolve, 1000))
  const response = await axios.get<IData[]>('https://xxxxx').then(res => res.data);
  console.log('Latest response is', response);
  return response;
}

// 直接从useQuery取返回的数据,不需要自己维护state
const { data: fetchData = [], isLoading, error } = useQuery(queryKey, getAlertsFunction, {
  refetchOnWindowFocus: true,
});
// 如果需要displayData可以直接基于fetchData派生,或者用useMemo缓存
const displayData = fetchData;

这个方案从根源上避免了自行编写setState带来的卸载后更新问题,React Query内部会处理组件卸载后的请求取消、状态更新拦截逻辑。

现有代码兼容修改方案

如果你一定要保留自行维护的state,按如下步骤修改:

  1. 把axios的cancelSource实例逻辑调整为可被清理函数访问
  2. 组件卸载时主动取消未完成的请求,同时调整useEffect的依赖项避免重复触发清理
    修改后代码如下:
const [fetchData, setFetchData] = React.useState<IData[]>([]);
const [displayData, setDisplayData] = React.useState<IData[]>([]);
const queryKey = 'planes';

const getAlertsFunction = async (): Promise<any> => {
  const source = axios.CancelToken.source()
  // 把cancel方法挂载到queryFn上,方便后续调用
  getAlertsFunction.cancel = () => source.cancel('Query cancelled')

  await new Promise(resolve => setTimeout(resolve, 1000))
  try {
    const response = await axios.get('https://xxxxx', { cancelToken: source.token }).then(res => res.data);
    setFetchData(response);
    setDisplayData(response);
    console.log('Latest response is', response);
    return response;
  } catch (err) {
    // 捕获取消请求的报错,避免异常抛出
    if (axios.isCancel(err)) return;
    throw err;
  }
}

const queryInfo: any = useQuery(queryKey, getAlertsFunction, {
  refetchOnWindowFocus: true,
});

useEffect(() => {
  console.log('Mounted');
  return () => {
    console.log('Un-mounted');
    // 组件卸载时调用取消请求的方法
    getAlertsFunction?.cancel?.()
  };
}, []);

注意这里useEffect的依赖项要改为空数组,你原来写的[displayData]会导致每次displayData变化都重新执行effect、生成新的清理函数,不符合卸载时只执行一次清理的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 14:36:07