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

React模态框卸载后while循环未终止的原因咨询

模态框卸载后while循环仍持续运行的原因及解决方法

问题概述

在模态框组件内用while循环实现轮询逻辑,关闭模态框(组件卸载)后,控制台日志仍持续输出,循环并未终止。原以为条件渲染的组件卸载后循环会自动中止,疑惑为何循环还在运行。

相关代码

import React from "react";
import "./styles.css";

const Modal = () => {
  const wait = async (ms = 1000) => {
    return new Promise((resolve) => setTimeout(resolve, ms));
  };

  let count = 0;
  const pollIncrement = async () => {
    while (count < 100) {
      await wait(2000);
      console.log(++count);
    }
    return count;
  };

  React.useEffect(() => {
    pollIncrement();
  }, []);

  return (
    <div
      style={{
        background: "blue",
        color: "white",
        width: "400px",
        height: "400px"
      }}
    >
      {count}
    </div>
  );
};

export default function App() {
  const [isModalOpen, setIsModalOpen] = React.useState(false);

  return (
    <>
      <button onClick={() => setIsModalOpen(!isModalOpen)}>open modal</button>
      {isModalOpen && <Modal />}
    </>
  );
}

原因分析

React组件卸载只会清理自身的React相关资源(比如状态、useEffect的清理逻辑等),但无法直接终止已经启动的异步任务或循环。你在useEffect中调用的pollIncrement函数,一旦开始执行就会进入异步等待流程(await wait(2000)),这个异步任务会被放入JavaScript的事件队列中,即使组件卸载,队列里的任务仍会按照既定逻辑执行,直到循环条件不满足。

简单来说:组件卸载不影响已经运行的异步代码,循环会继续执行,因为JavaScript的异步任务不受React组件生命周期的直接管控。

解决方法

要在组件卸载时终止循环,需要添加终止标识,并借助useEffect的清理函数触发标识,让循环条件失效。

方案1:使用布尔终止标识

修改后的Modal组件代码:

const Modal = () => {
  const wait = async (ms = 1000) => {
    return new Promise((resolve) => setTimeout(resolve, ms));
  };

  let count = 0;
  // 定义终止标识
  let isUnmounted = false;

  const pollIncrement = async () => {
    // 将终止标识加入循环判断条件
    while (count < 100 && !isUnmounted) {
      await wait(2000);
      // 等待结束后再检查一次,避免等待期间组件已卸载
      if (isUnmounted) break;
      console.log(++count);
    }
    return count;
  };

  React.useEffect(() => {
    pollIncrement();
    // 组件卸载时设置终止标识
    return () => {
      isUnmounted = true;
    };
  }, []);

  return (
    <div
      style={{
        background: "blue",
        color: "white",
        width: "400px",
        height: "400px"
      }}
    >
      {count}
    </div>
  );
};

方案2:使用AbortController(更适合真实API请求场景)

AbortController是浏览器提供的专门用于终止异步任务的API,能更可靠地中断请求或定时器:

const Modal = () => {
  const wait = (ms = 1000, signal) => {
    return new Promise((resolve, reject) => {
      const timeout = setTimeout(resolve, ms);
      // 监听中止信号,触发时清除定时器并拒绝Promise
      signal.addEventListener('abort', () => {
        clearTimeout(timeout);
        reject(new Error('Polling aborted'));
      });
    });
  };

  let count = 0;

  const pollIncrement = async (signal) => {
    try {
      while (count < 100) {
        await wait(2000, signal);
        console.log(++count);
      }
    } catch (err) {
      if (err.name === 'AbortError') {
        console.log('Polling stopped');
      }
    }
    return count;
  };

  React.useEffect(() => {
    const controller = new AbortController();
    pollIncrement(controller.signal);
    // 组件卸载时发送中止信号
    return () => {
      controller.abort();
    };
  }, []);

  return (
    <div
      style={{
        background: "blue",
        color: "white",
        width: "400px",
        height: "400px"
      }}
    >
      {count}
    </div>
  );
};

内容的提问来源于stack exchange,提问作者gywn.olivia

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 17:45:26