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会先执行一次副作用函数,立刻调用它的清理函数,然后再执行一次副作用函数——这是模拟「挂载后立刻卸载再重新挂载」的场景,帮你发现未及时清理的副作用(比如泄漏的订阅、定时器)。
对应输出的一步步拆解
现在我们把实际输出和严格模式的行为对应起来:
连续两次执行组件函数:
- 第一次调用:初始化
x=1→ 打印outside 1→ 定义useEffect→ 打印end of fn 1 - 第二次调用:再次初始化
x=1→ 打印outside 1→ 定义useEffect→ 打印end of fn 1
(这两步是连续完成的,所以你先看到前四行输出)
- 第一次调用:初始化
处理副作用的模拟挂载-卸载-挂载流程:
- 第一次执行副作用函数:此时捕获的是第二次组件函数里的
x=1,打印1,然后把x改成2 - 立刻调用清理函数:此时
x已经被改成2,所以打印destruct 2 - 第二次执行副作用函数:因为并没有真的卸载组件,闭包保留了修改后的
x,所以打印2
- 第一次执行副作用函数:此时捕获的是第二次组件函数里的
为什么你的预期不对?
你以为严格模式会先完成一次完整的挂载(组件执行→副作用执行→卸载),再重新执行组件函数完成第二次挂载,但实际上严格模式的重复逻辑是:
- 先连续执行两次组件函数(检测纯函数)
- 再模拟「挂载→卸载→挂载」的副作用流程(但组件实例并没有真的被销毁,所以闭包变量会被保留)
另外要注意:这些重复执行的行为只在开发环境生效,生产环境下会恢复正常的单次执行流程。
备注:内容来源于stack exchange,提问作者Robin Roy
相关产品推荐
相关产品推荐

