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

React18 Strict Mode下如何仅调用一次清理函数?

React18 Strict Mode下避免清理函数重复执行的兼容方案

React18的Strict Mode在开发环境会触发组件mount->unmount->mount的流程,目的是提前暴露组件在重复挂载/卸载时可能出现的问题。但这种行为会导致某些依赖清理时机的代码(比如API请求的中止逻辑)意外触发,像你给出的代码中,全局的AbortController会在第一次模拟卸载时被中止,导致第二次挂载后的API请求刚发起就失败。

下面是两种兼容开发与生产环境的解决方法:

方案一:将资源实例放到useEffect内部(推荐)

把AbortController的创建移到useEffect回调内部,这样每次组件挂载都会生成新的控制器实例,模拟卸载时只会中止当前实例对应的请求,第二次挂载会使用全新的控制器,不会影响新的API调用:

import { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    // 每次挂载都创建新的AbortController
    const abortController = new AbortController();

    // 发起API请求,传入signal
    fetch('/api/target-data', {
      signal: abortController.signal
    })
    .then(res => res.json())
    .then(data => {
      // 处理请求结果
      console.log('请求成功:', data);
    })
    .catch(err => {
      // 忽略中止错误,只处理其他异常
      if (err.name !== 'AbortError') {
        console.error('请求失败:', err);
      }
    });

    // 清理函数只针对当前挂载周期的控制器
    return () => {
      abortController.abort();
    };
  }, []);

  return <div>组件内容</div>;
}

这种方式完全符合React Strict Mode的设计预期,让组件具备可重入性,无论是开发还是生产环境都能正常工作。

方案二:用Ref标记真实卸载场景

如果你必须将资源实例放在useEffect外部,可以通过Ref来区分Strict Mode的模拟卸载和真实组件卸载:

import { useEffect, useRef } from 'react';

function MyComponent() {
  const abortController = new AbortController();
  // 标记是否为真实卸载
  const isPermanentUnmount = useRef(false);

  useEffect(() => {
    // 发起API请求
    fetch('/api/target-data', {
      signal: abortController.signal
    })
    .then(res => res.json())
    .then(data => console.log('请求成功:', data))
    .catch(err => {
      if (err.name !== 'AbortError') {
        console.error('请求失败:', err);
      }
    });

    return () => {
      // 仅在真实卸载时中止请求
      if (isPermanentUnmount.current) {
        abortController.abort();
      }
    };
  }, []);

  // 监听组件是否真的要卸载(Strict Mode模拟卸载不会触发这个useEffect的清理)
  useEffect(() => {
    return () => {
      isPermanentUnmount.current = true;
    };
  }, []);

  return <div>组件内容</div>;
}

这种方式通过额外的Ref标记真实卸载状态,避免模拟卸载时误中止请求,但相比方案一,代码复杂度更高,仅在无法重构资源实例位置时使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 05:46:07