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

React Strict Mode下setTimeout触发两次的原因排查

React Strict Mode下setTimeout重复触发的问题

组件代码

function ExportViewerPage(): JSX.Element {
  const [hasPrinted, setHasPrinted] = useState(false);

  if (!hasPrinted) {
    setHasPrinted(true);
    console.log("Inside if loop, hasPrinted: ", hasPrinted)
    setTimeout(() => {
      console.log("Executing setTimeout: ", hasPrinted)
    });
  }

  return (<div>Page</div>)
}

export default ExportViewerPage;

现象描述

预期setTimeout里的代码只执行一次,但实际控制台输出两次:

Inside if loop, hasPrinted:  false
Executing setTimeout:  false
Executing setTimeout:  false

奇怪的是setTimeout上方的日志只输出一次,说明定时器只被调用了一次,但回调却执行了两次。关闭Next.js配置文件next.config.js中的reactStrictMode后,问题消失,定时器仅触发一次。

原因解析

这是React Strict Mode在开发环境下的故意设计,目的是帮开发者排查潜在的代码问题:

  1. Strict Mode会重复执行组件渲染函数、状态更新相关逻辑,以此检测非纯函数操作(比如在渲染里直接改状态、创建副作用)。
  2. 你的代码直接在渲染流程里调用setHasPrinted(true)和创建定时器,违反了React“渲染函数必须是纯函数”的规则。第一次渲染时创建了定时器,Strict Mode触发第二次渲染时,虽然hasPrinted已经变为true不会再进入if分支,但第一次创建的定时器没有被清理,加上Strict Mode对异步操作的检测机制,最终导致回调被执行两次。
  3. 关闭Strict Mode后,React不再执行重复渲染流程,所以问题不会出现。

解决方案

把副作用逻辑放到useEffect中,并添加清理函数,确保副作用能被正确处理:

import { useState, useEffect } from 'react';

function ExportViewerPage(): JSX.Element {
  const [hasPrinted, setHasPrinted] = useState(false);

  useEffect(() => {
    if (!hasPrinted) {
      setHasPrinted(true);
      console.log("Inside useEffect, hasPrinted: ", hasPrinted);
      const timer = setTimeout(() => {
        console.log("Executing setTimeout: ", hasPrinted);
      });
      // 组件卸载或依赖变化时清理定时器
      return () => clearTimeout(timer);
    }
  }, [hasPrinted]);

  return (<div>Page</div>)
}

export default ExportViewerPage;

这样处理后,即使在Strict Mode下,useEffect的清理函数会先清除第一次创建的定时器,第二次执行useEffect时hasPrinted已经是true,不会再创建新的定时器,最终只会触发一次回调。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 04:07:04