← 中文资源

简体中文版本

React 警告 — 在已卸载组件上更新状态

通过在卸载时中止进行中的工作并清理订阅,修复 “Can't perform a React state update on an unmounted component”。

阅读英文原文

症状

错误“React 警告 — 在已卸载组件上更新状态”指向一个明确的失效模式。下面依次给出症状、定位该原因的检查命令、根本原因、修复方案,以及证明修复有效的验证步骤。

可观察到的症状包括:

  • React 警告在已卸载组件上更新状态,提示内存泄漏
  • 快速离开页面后出现该警告
  • fetch 或定时器在组件消失后完成

诊断

先在隔离环境中复现故障,再按顺序执行以下检查,逐步缩小范围。

步骤 1

grep -rn "then(.*set[A-Z]\|await .*set[A-Z]" src/

任何在无挂载防护下由异步完成触达的 setState 都是候选。

步骤 2

git diff src/hooks/useData.js

AbortController 配合清理返回可中止请求并阻止滞后更新。

常见诱因(按出现频率排序):

  • 异步回调在卸载后调用 setState
  • 订阅或定时器未在 effect 清理函数中释放
  • effect 重跑时 promise 链没有取消机制

根因

React 组件可能随时被卸载,但异步工作若不取消会继续。当其回调在卸载后触发,setState 会指向已不存在的组件,造成内存泄漏并告警。

修复

在 effect 清理函数中取消异步工作(fetch 用 AbortController、定时器用 clearInterval、订阅则退订),确保卸载后不会再有更新。

注意:用状态记录 mounted 标志本身就是一次状态更新;应优先采用外部取消而非追踪 mounted。

useEffect(() => {
  const controller = new AbortController();
  fetch(`/api/items`, { signal: controller.signal })
    .then((r) => r.json())
    .then(setItems)
    .catch((err) => { if (err.name !== 'AbortError') console.error(err); });

  return () => controller.abort(); // cleanup on unmount or re-run
}, []);

// timers
useEffect(() => {
  const id = setInterval(tick, 1000);
  return () => clearInterval(id);
}, []);

验证

在请求进行中离开页面并确认无警告;在日志中断言 AbortController 的信号已中止 fetch。

修复后补充一条回归测试,复现原始症状,防止问题再次出现。

这类故障通常是环境问题而非纯逻辑错误,因此在缺陷记录中应写明完整命令、环境变量与工作目录。请用最小的脚本复现,待最小用例清楚后再扩大范围。为这类故障准备一份简短的排查清单,能显著减少下次排障时间。