You need to enable JavaScript to run this app. React Native中useEffect调用API触发未挂载组件状态更新警告的解决方法
优惠活动
大模型
产品
解决方案
定价
更多

React Native中useEffect调用API触发未挂载组件状态更新警告的解决方法

报错根源

切换页面导致HomeScreen组件被卸载时,未完成的异步API请求、持续运行的时长统计定时器仍在后台执行,执行完成后触发setState修改已卸载组件的状态,就会弹出「Can't perform a React state update on an unmounted component」的警告。

具体修复步骤

1. 增加组件挂载状态标记

用useRef存储组件的挂载状态(不会触发重渲染,适合存这类逻辑判断标记),所有状态更新操作执行前先判断组件是否还处于挂载状态,未挂载则直接跳过状态修改。

// 标记组件是否仍处于挂载状态
const isMountedRef = useRef(true);

2. 重构定时器存储逻辑

原代码把定时器ID存在state里的写法存在时序bug:setDeleteExecutionCounting是异步执行的,后续引用时很容易拿到旧的定时器值,导致定时器无法被正常清理。将定时器ID直接存在ref中,实现统一管理、统一清理。

// 存储正在运行的定时器ID
const timerRef = useRef(null);

3. 增加卸载清理逻辑

编写仅在组件挂载/卸载时执行的副作用,组件卸载时完成两件核心清理工作:将挂载标记设为false、清除所有正在运行的定时器,彻底切断后台触发状态更新的源头。

useEffect(() => {
  isMountedRef.current = true;
  return () => {
    // 组件卸载时执行清理
    isMountedRef.current = false;
    if (timerRef.current) {
      clearInterval(timerRef.current);
      timerRef.current = null;
    }
  }
}, []);

4. 改造loadData的状态更新逻辑

所有异步回调内的状态更新,先判断组件是否还挂载,未挂载则直接返回不执行setState;同时替换原代码的串行then链,用finally做统一的收尾清理,避免清理逻辑漏执行。

const loadData = useCallback(() => {
  // 先清理上一次未结束的定时器,避免多个定时器并行运行
  if (timerRef.current) {
    clearInterval(timerRef.current);
  }
  // 启动请求时长统计定时器
  timerRef.current = setInterval(() => {
    // 状态更新前先判断挂载状态
    if (isMountedRef.current) {
      setExecutionDuration(prev => prev + 1);
    }
  }, 2000);

  setError(false);
  setLoading(true);

  getRowsFromDb(Id)
    .then(response => {
      if (!isMountedRef.current) return;
      if (response.status === 204) {
        setData({ rows: [], count: 0 });
      } else {
        setData(response.data);
      }
    })
    .catch(err => {
      if (!isMountedRef.current) return;
      setError(true);
    })
    .finally(() => {
      if (!isMountedRef.current) return;
      // 收尾清理定时器
      if (timerRef.current) {
        clearInterval(timerRef.current);
        timerRef.current = null;
      }
      setExecutionDuration(0);
      setLoading(false);
    });
}, [Id]);
可选优化项
  • 原代码同时在useEffect内直接调用loadData、又绑定了屏幕focus事件,组件首次挂载时会触发两次重复的API请求,可以去掉那个直接调用loadData的useEffect,让所有刷新逻辑统一走focus事件触发,减少无效请求。
  • 原代码在loading渲染判断里直接读取executionDurationRef.current的值,ref变化不会触发重渲染,会导致超时提示不能及时更新,直接读取state里的executionDuration值即可。
  • 如果用Axios发请求,可配合AbortController在组件卸载时直接取消未完成的请求,从源头减少无效网络请求,降低资源消耗。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.02 18:54:04