React中setInterval的console.log无法反映更新后的状态
React useState闭包陷阱导致setInterval与onClick调用同一函数结果不同
问题代码
"use client" import React, { useEffect } from 'react' import { useState } from 'react'; export default function page() { const [count, setCount] = useState(0); const handleClick = () => { setCount((count)=>count + 1); }; function checkVal(){ console.log(count) } useEffect(()=>{ setInterval(checkVal,1000) },[]) return ( <div> <button onClick={handleClick}>{count}</button> <button onClick={checkVal}>clickmetocheck</button> </div> ) }
问题现象
该组件通过两种方式调用checkVal函数输出count值:
- 通过
setInterval定时调用 - 通过按钮
onClick事件触发调用
实际表现存在差异:
setInterval调用的checkVal始终打印初始值0,即使点击按钮更新了count- 点击按钮触发的
checkVal能正确打印更新后的count
原因分析
这是典型的React函数组件闭包陷阱,核心逻辑如下:
- 函数组件每次状态更新都会触发重新渲染,每次渲染都会创建全新的函数实例和变量快照。
- 组件首次渲染时,
count为初始值0,此时定义的checkVal函数会捕获这个0的count变量。 useEffect的依赖数组是空数组,只会在组件挂载时执行一次,里面的setInterval会一直引用首次渲染时的checkVal函数——这个函数里的count永远是首次渲染的快照值0,不会随着后续状态更新而改变。- 而点击按钮触发的
checkVal,是组件每次更新后重新创建的新函数实例,它捕获的是当前渲染周期的count快照值,所以能正确输出最新的count。
解决方案
方案一:使用useRef保存最新值
通过useRef保存count的最新值,让checkVal读取ref.current来获取实时状态:
"use client" import React, { useEffect, useRef } from 'react' import { useState } from 'react'; export default function page() { const [count, setCount] = useState(0); const countRef = useRef(count); // 每次count更新时同步到ref useEffect(() => { countRef.current = count; }, [count]); const handleClick = () => { setCount((count)=>count + 1); }; function checkVal(){ console.log(countRef.current) } useEffect(()=>{ const timer = setInterval(checkVal,1000) return () => clearInterval(timer); // 组件卸载时清理定时器 },[]) return ( <div> <button onClick={handleClick}>{count}</button> <button onClick={checkVal}>clickmetocheck</button> </div> ) }
方案二:让useEffect依赖count更新定时器
将count加入useEffect的依赖数组,每次count更新时重新创建定时器,确保定时器引用的是最新的checkVal函数:
"use client" import React, { useEffect } from 'react' import { useState } from 'react'; export default function page() { const [count, setCount] = useState(0); const handleClick = () => { setCount((count)=>count + 1); }; function checkVal(){ console.log(count) } useEffect(()=>{ const timer = setInterval(checkVal,1000) return () => clearInterval(timer); // 每次更新前清理旧定时器 },[count]) return ( <div> <button onClick={handleClick}>{count}</button> <button onClick={checkVal}>clickmetocheck</button> </div> ) }
内容的提问来源于stack exchange,提问作者Kanna Kim
相关产品推荐
相关产品推荐

