React useEffect清理函数触发clearInterval的时机与逻辑疑问
useEffect清理函数的执行时机
针对你代码里的写法,清理函数只会在两个场景执行:
- 当依赖数组
[tenzie, isTimerActive]里的任意值发生变化,需要重新执行useEffect内的逻辑前,会先执行上一次effect返回的清理函数 - 当绑定该useEffect的组件从DOM中卸载(组件销毁)时,会执行最后一次effect对应的清理函数,避免内存泄漏
注意:组件普通重渲染(比如你的time状态每秒更新触发的重渲染)如果没有引起依赖项变化,是不会触发effect重跑,也不会执行清理函数的。
为什么清理逻辑存在,计时器仍能正常工作
我们顺着你的业务逻辑走一遍执行流程就能明白:
- 页面初始加载时,
isTimerActive=false、tenzie=false,第一次执行effect:不满足isTimerActive && !tenzie的判断条件,局部变量timer为null,此时返回的清理函数执行clearInterval(null)属于无操作,不会产生任何影响。 - 用户点击按钮将
isTimerActive设为true,依赖项变化触发effect重跑:- 先执行上一轮effect的清理函数:上一轮的timer值是null,clearInterval无任何效果
- 再执行本轮effect逻辑:判断条件成立,创建
setInterval定时器,将定时器id存入本轮effect上下文的局部变量timer中,开始每秒累加time值
- 游戏进行过程中,每秒触发的
setTime只会让组件重渲染,但time不在该useEffect的依赖数组里,tenzie和isTimerActive的值也没有变化,所以effect不会重跑,清理函数也不会执行,定时器会持续正常运行。 - 用户完成游戏将
tenzie设为true,依赖项再次变化触发effect重跑:- 先执行上一轮effect的清理函数:此时上一轮上下文里存的timer就是正在运行的定时器id,
clearInterval(timer)会准确停掉正在跑的计时逻辑 - 再执行本轮effect逻辑:因为
tenzie=true,判断条件不成立,不会创建新的定时器,计时自然停止,完全符合业务预期。
- 先执行上一轮effect的清理函数:此时上一轮上下文里存的timer就是正在运行的定时器id,
这里的核心是:每次effect执行时,内部的变量都是属于当前执行上下文的独立值,清理函数捕获的也是对应轮次的timer变量,不会出现错清、漏清的问题,你的写法本身就是React推荐的定时器标准写法。
内容的提问来源于stack exchange,提问作者Sumit
相关产品推荐
相关产品推荐

