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

为何重新打开已关闭标签页时React Hooks清理函数会执行?(Safari独有)

问题分析与解决方案

问题根源

Safari在页面隐藏(如关闭标签页)后恢复显示的场景下,React组件清理函数的执行时机与Chrome/Firefox存在差异。当你撤销标签页关闭操作回到页面时,之前已卸载组件的abortController.abort()会延迟执行,而此时新挂载的组件已发起请求。由于Safari对AbortController和fetch请求的特殊处理逻辑,旧的中止信号可能意外干扰到新请求的执行。

修复方案1:添加挂载状态检查 + 精准依赖数组

通过新增挂载状态标志,确保仅在组件处于挂载状态时处理请求结果;同时将依赖数组从整个props.match.params对象改为具体属性,让React能更准确地触发Effect更新。

React.useEffect(() => {
  let isMounted = true;
  const abortController = new AbortController();

  fetch(`${URL}${API.mediaDetails}`
    + `?id=${encodeURIComponent(props.match.params.id)}`
    + `&type=${encodeURIComponent(props.match.params.type)}`
    + `&lang=${encodeURIComponent(props.match.params.locale?.slice(0, 2))}`, {
      signal: abortController.signal,
      timeout: DOWNLOAD_HUB_REQ_TIMEOUT
    })
    .then((res) => res.json())
    .then((res) => {
      if (!isMounted) return;
      res.type = props.match.params.type;
      setDetails(res);
    })
    .catch((err) => {
      if (!isMounted) return;
      setDetails(null);
      setError(err);
    });

  return () => {
    isMounted = false;
    abortController.abort();
  };
}, [props.match.params.id, props.match.params.type, props.match.params.locale]);

修复方案2:用Ref跟踪当前控制器

通过useRef存储当前的AbortController实例,每次Effect执行时先中止之前的请求,确保新请求不会被旧的清理操作干扰。

const abortControllerRef = React.useRef(null);

React.useEffect(() => {
  // 中止之前未完成的请求
  if (abortControllerRef.current) {
    abortControllerRef.current.abort();
  }

  const abortController = new AbortController();
  abortControllerRef.current = abortController;

  fetch(`${URL}${API.mediaDetails}`
    + `?id=${encodeURIComponent(props.match.params.id)}`
    + `&type=${encodeURIComponent(props.match.params.type)}`
    + `&lang=${encodeURIComponent(props.match.params.locale?.slice(0, 2))}`, {
      signal: abortController.signal,
      timeout: DOWNLOAD_HUB_REQ_TIMEOUT
    })
    .then((res) => res.json())
    .then((res) => {
      // 仅处理当前控制器对应的请求结果
      if (abortControllerRef.current !== abortController) return;
      res.type = props.match.params.type;
      setDetails(res);
    })
    .catch((err) => {
      if (abortControllerRef.current !== abortController) return;
      setDetails(null);
      setError(err);
    });

  return () => {
    // 仅中止当前控制器对应的请求
    if (abortControllerRef.current === abortController) {
      abortController.abort();
      abortControllerRef.current = null;
    }
  };
}, [props.match.params.id, props.match.params.type, props.match.params.locale]);

方案说明

两种方案都解决了Safari中旧清理函数干扰新请求的问题:

  • 方案1通过挂载状态过滤无效的状态更新,同时精准的依赖数组确保Effect仅在必要时重新执行。
  • 方案2通过Ref跟踪当前请求的控制器,确保旧的中止操作只作用于旧请求,不会影响新请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 02:17:35