React中useEffect定时器调用函数时count控制台输出异常问题
为什么控制台打印的count始终为0,但页面显示正常递增?
问题代码
import React, { useState, useEffect } from 'react' function Counter() { const [count, setCount] = useState(0) const incCount = () => { console.log("count ", count) // setCount(count + 1) setCount(x => x + 1) } useEffect(() => { console.log('use effect called') const intervalId = setInterval(() => incCount(), 1000) return() => { // clearInterval(intervalId); console.log("clear interval called") } }, []) return ( <div> {count} </div> ) } export default Counter
问题描述
页面上的count按预期每秒递增1,但控制台每次仅输出0。incCount函数确实每秒执行一次,控制台打印的count始终为0,与页面显示不符。
原因分析
这是闭包陷阱导致的:
- 你的
useEffect依赖项是空数组[],只会在组件挂载时执行一次。挂载时创建的setInterval回调,引用的是组件第一次渲染时生成的incCount函数。 - 这个初始版本的
incCount捕获了初始状态的count(值为0),之后组件每次重新渲染都会生成新的incCount函数,但定时器里始终用的是旧的那个,所以每次打印的都是初始的0。 - 而页面显示正常,是因为你用了函数式更新
setCount(x => x + 1)——这种方式会直接基于React内部保存的最新状态值计算新状态,不受闭包捕获的旧值影响。
解决方案
方法一:用useRef保存最新状态值
通过useRef同步状态的最新值,让incCount能访问到实时的count:
import React, { useState, useEffect, useRef } from 'react' function Counter() { const [count, setCount] = useState(0) const countRef = useRef(count) // 每次count更新时同步ref的current值 useEffect(() => { countRef.current = count }, [count]) const incCount = () => { console.log("count ", countRef.current) // 从ref获取最新值 setCount(x => x + 1) } useEffect(() => { const intervalId = setInterval(() => incCount(), 1000) return() => { clearInterval(intervalId); // 必须清理定时器,避免内存泄漏 } }, []) return ( <div> {count} </div> ) } export default Counter
方法二:用useCallback缓存函数并更新依赖
把incCount用useCallback缓存,同时加入useEffect的依赖项,让函数更新时重新创建定时器:
import React, { useState, useEffect, useCallback } from 'react' function Counter() { const [count, setCount] = useState(0) // 用useCallback缓存函数,依赖count更新 const incCount = useCallback(() => { console.log("count ", count) setCount(x => x + 1) }, [count]) useEffect(() => { const intervalId = setInterval(() => incCount(), 1000) return() => { clearInterval(intervalId); // 清理定时器 } }, [incCount]) // 依赖incCount,函数更新时重新执行useEffect return ( <div> {count} </div> ) } export default Counter
额外提醒
原代码里注释掉了clearInterval(intervalId),这会导致组件卸载后定时器仍在运行,造成内存泄漏,一定要保留清理逻辑。
内容的提问来源于stack exchange,提问作者Rakesh yadav
相关产品推荐
相关产品推荐

