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

React严格模式控制流程及组件执行输出疑惑

React严格模式控制流程及组件执行输出疑惑

你遇到的这个问题其实是React严格模式下的开发环境专属重复执行机制导致的,很多刚接触严格模式的开发者都会踩这个坑,我来一步步给你拆解清楚。

首先先还原你的代码:

import { useEffect } from "react";

export default function Learn() {
    let x = 1;
    console.log("outside ", x)
    useEffect(() => {
        console.log(x);
        x = 2;

        return () => console.log("destruct", x)
    }, []);
    console.log("end of fn ", x)
    return (
        <>
            <p>value = {x}</p>
        </>
    );
}

你实际得到的输出:

outside  1
end of fn  1
outside  1
end of fn  1
1
destruct 2
2

你的预期输出:

outside 1
end of fn 1
1
destruct 2
outside 1
end of fn 1
1

核心问题:你对严格模式的执行流程理解错了

严格模式并不是“先完整挂载一次组件,卸载后再重新挂载一次”,而是在开发环境下刻意做了两个关键操作,用来帮你提前发现潜在bug:

  • 组件顶层函数会被连续调用两次:这是为了检测你的组件函数是不是纯函数(比如如果组件函数里修改了全局变量,两次执行就会暴露问题)。
  • 挂载时的副作用会被「执行→清理→再执行」:也就是对于空依赖的useEffect,React会先执行一次副作用函数,立刻调用它的清理函数,然后再执行一次副作用函数——这是模拟「挂载后立刻卸载再重新挂载」的场景,帮你发现未及时清理的副作用(比如泄漏的订阅、定时器)。

对应输出的一步步拆解

现在我们把实际输出和严格模式的行为对应起来:

  1. 连续两次执行组件函数:

    • 第一次调用:初始化x=1 → 打印outside 1 → 定义useEffect → 打印end of fn 1
    • 第二次调用:再次初始化x=1 → 打印outside 1 → 定义useEffect → 打印end of fn 1
      (这两步是连续完成的,所以你先看到前四行输出)
  2. 处理副作用的模拟挂载-卸载-挂载流程:

    • 第一次执行副作用函数:此时捕获的是第二次组件函数里的x=1,打印1,然后把x改成2
    • 立刻调用清理函数:此时x已经被改成2,所以打印destruct 2
    • 第二次执行副作用函数:因为并没有真的卸载组件,闭包保留了修改后的x,所以打印2

为什么你的预期不对?

你以为严格模式会先完成一次完整的挂载(组件执行→副作用执行→卸载),再重新执行组件函数完成第二次挂载,但实际上严格模式的重复逻辑是:

  • 先连续执行两次组件函数(检测纯函数)
  • 再模拟「挂载→卸载→挂载」的副作用流程(但组件实例并没有真的被销毁,所以闭包变量会被保留)

另外要注意:这些重复执行的行为只在开发环境生效,生产环境下会恢复正常的单次执行流程。

备注:内容来源于stack exchange,提问作者Robin Roy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 09:18:01