组件卸载时应使用AbortController中止请求,避免setState报错和内存泄漏;现代方案推荐在useEffect中创建控制器、传入signal、清理时abort,并配合错误过滤,Axios 1.0+已原生支持该机制。

组件卸载时取消未完成的请求,核心是避免 setState 或状态更新发生在已卸载的组件上,防止 React 报错(如 “Can’t perform a React state update on an unmounted component”)和内存泄漏。现代方案主要依赖 AbortController 和 React 的清理机制。
使用 AbortController 主动中止 fetch 请求
AbortController 是浏览器原生支持的标准 API,适用于 fetch。在组件挂载时创建控制器,在卸载时调用 abort(),fetch 会立即拒绝 Promise。
- 在
useEffect中创建AbortController实例 - 将
controller.signal传入fetch的signal选项 - 在
useEffect清理函数中调用controller.abort() - 捕获
AbortError(可选),避免未处理的 rejected promise
示例:
useEffect(() => {
const controller = new AbortController();
const loadData = async () => {
try {
const res = await fetch('/api/data', { signal: controller.signal });
const data = await res.json();
setData(data);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err.message);
}
// 忽略中止错误,不 setState
}
};
loadData();
return () => controller.abort(); // 卸载时中止
}, []);
对 Axios 请求使用 CancelToken(旧版)或 AbortController(推荐)
Axios 1.0+ 已原生支持 AbortController,不再推荐使用已废弃的 CancelToken。
立即学习“Java免费学习笔记(深入)”;
- 直接传入
signal:与 fetch 用法一致 - 无需额外配置拦截器或自定义 cancel 逻辑
- 中止后 axios 请求 Promise 会以
AbortError拒绝
示例:
useEffect(() => {
const controller = new AbortController();
axios.get('/api/items', {
signal: controller.signal
})
.then(res => setData(res.data))
.catch(err => {
if (axios.isCancel(err)) return; // 旧写法,v1.0+ 改用 AbortError 判断
if (err.name === 'AbortError') return;
setError(err.message);
});
return () => controller.abort();
}, []);
封装自定义 Hook 统一管理请求生命周期
把中止逻辑抽离为可复用 hook,提升可维护性,也便于统一错误处理和 loading 状态。
- 返回
loading、data、error和触发请求的函数 - 每次调用请求函数时自动创建新
AbortController - 确保仅最后一次请求的结果被采纳(可选:结合 ref 判断是否“当前有效”)
简化版示意:
function useApi() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const request = useCallback(async (url, options = {}) => {
const controller = new AbortController();
setLoading(true);
setError(null);
try {
const res = await fetch(url, {
...options,
signal: controller.signal
});
const result = await res.json();
setData(result);
return result;
} catch (err) {
if (err.name !== 'AbortError') {
setError(err.message);
}
} finally {
setLoading(false);
}
return () => controller.abort();
}, []);
return { data, loading, error, request };
}
注意 useEffect 清理时机与异步执行顺序
useEffect 清理函数会在组件卸载前同步执行,但异步操作(如 fetch.then)可能在清理之后才运行。因此不能仅靠组件状态判断是否已卸载 —— 必须依赖 AbortController 中止网络层,再配合错误过滤。
- 不要用
isMountedref 模拟(不必要且易出错) - 避免在 then/catch 中无条件调用
setState - 中止信号必须传给请求本身,而不是只靠 JS 层跳过 setState
不推荐的写法:
// ❌ 错误:网络请求仍在进行,可能触发 setState 报错
let isMounted = true;
useEffect(() => {
fetch('/api').then(res => res.json()).then(data => {
if (isMounted) setData(data); // 仍可能漏掉
});
return () => { isMounted = false };
}, []);
不复杂但容易忽略。


















