React中如何正确取消未完成的Ajax请求?

公孙万华 阅读 66

我在用useEffect做数据请求,切换页面时老报“Can’t perform a React state update on an unmounted component”错误。试过AbortController但好像没生效,是不是哪里写错了?

useEffect(() => {
  const controller = new AbortController();
  fetch('/api/data', { signal: controller.signal })
    .then(res => res.json())
    .then(data => setData(data));
  
  return () => controller.abort();
}, []);
我来解答 赞 13 收藏
二维码
手机扫码查看
2 条解答
シ文茹
シ文茹 Lv1
问题出在两处。

第一,你的代码没有处理abort后抛出的AbortError。虽然这不会直接导致你看到的那个警告,但console里应该会报unhandled promise rejection。

第二,更关键的是:即使调用了abort(),请求发出去后响应回来时组件可能已经unmount了,这时候setData照常执行,照样触发那个警告。abort()只是告诉浏览器取消请求,但它不能阻止响应回来后执行的then回调。

正确的写法是这样的:

useEffect(() => {
let isMounted = true;
const controller = new AbortController();

fetch('/api/data', { signal: controller.signal })
.then(res => res.json())
.then(data => {
if (isMounted) {
setData(data);
}
})
.catch(err => {
if (err.name !== 'AbortError') {
console.error(err);
}
});

return () => {
isMounted = false;
controller.abort();
};
}, []);


核心改动就两点:

一是用isMounted这个flag在setState前判断组件是否还挂着。二是加了catch捕获AbortError,不然abort时会抛出个未处理的reject。

如果你用的是axios而不是fetch,那情况又不一样,axios自带cancel token,不过这是后话了。你先试试这个能不能解决你的问题。
点赞
2026-03-19 19:16
书生シ振巧
你的代码思路是对的,但漏了一个关键点:AbortError 没处理。

fetch 被 abort 时会抛出一个 AbortError,这个错误会打断 .then 链,但如果你不 catch 它,默认行为有点坑。而且更重要的,即使请求被取消了,如果此时组件已经卸载,setData 还是会触发那个 unmounted 错误。

正确写法是这样的:

useEffect(() => {
let isMounted = true;
const controller = new AbortController();

fetch('/api/data', { signal: controller.signal })
.then(res => res.json())
.then(data => {
if (isMounted) {
setData(data);
}
})
.catch(err => {
if (err.name !== 'AbortError') {
console.error('请求失败:', err);
}
});

return () => {
isMounted = false;
controller.abort();
};
}, []);


两个要点:

一是加个 isMounted 标记,在 setData 前判断组件是否还挂着。controller.abort() 只能阻止请求发出去或者拿到响应,但已经拿到响应后还是会往下走 then,所以必须自己判断。

二是 catch 里要区分 AbortError 和其他错误。AbortError 是正常的取消行为,不需要抛出来,其他错误(比如网络断了一般还是得处理一下)。

复制过去试试,基本就解决了。
点赞
2026-03-13 19:10