React中useState状态更新后值始终不变的原因咨询
为什么React状态在定时器中始终不更新?
我定义了如下状态:
const [number, SetNumber] = React.useState<number>(0)
随后在首次渲染的钩子中创建了更新函数和定时器:
const increaseNumber = () => { console.log(number); SetNumber(number + 1); }; React.useEffect(() => { setInterval(() => { increaseNumber(); }, 1000); }, []);
实际运行后,console.log(number)的输出始终是:
0 0 0 0 0
但我期望的输出是:
0 1 2 3 4
完整代码如下:
import * as React from 'react'; const App = () => { const [number, SetNumber] = React.useState<number>(0); const increaseNumber = () => { console.log(number); SetNumber(number + 1); }; React.useEffect(() => { setInterval(() => { increaseNumber(); }, 1000); }, []); return ( <div> <h1>{number}</h1> </div> ); }; export default App;
问题原因
这是React闭包陷阱导致的:
- 你的
useEffect依赖数组是空的,意味着它只会在组件首次渲染时执行一次,创建定时器。 - 首次渲染时,
increaseNumber函数通过闭包捕获了当时的number值(也就是初始值0)。 - 之后组件每次因为状态更新重新渲染时,都会创建一个新的
increaseNumber函数,但定时器里调用的始终是首次渲染时创建的那个旧函数——它里面的number永远是0。 - 所以每次调用
SetNumber(number + 1)其实都是SetNumber(0 + 1),状态被反复设置为1,但console.log输出的还是闭包里保存的0。
解决方案
有两种常用的修复方式:
方式1:使用函数式更新
直接给SetNumber传递一个函数,它会接收当前最新的状态值,这样就不需要依赖外部的number变量:
const increaseNumber = () => { // 用函数式更新获取最新状态 SetNumber(prevNumber => { console.log(prevNumber); return prevNumber + 1; }); }; React.useEffect(() => { setInterval(() => { increaseNumber(); }, 1000); }, []);
方式2:用useCallback包裹函数并更新依赖
通过useCallback让increaseNumber依赖number,同时把它加入useEffect的依赖数组,每次number更新时重新创建定时器(记得清理旧定时器):
import * as React from 'react'; const App = () => { const [number, SetNumber] = React.useState<number>(0); // 用useCallback包裹,依赖number const increaseNumber = React.useCallback(() => { console.log(number); SetNumber(number + 1); }, [number]); React.useEffect(() => { const timer = setInterval(() => { increaseNumber(); }, 1000); // 清理旧定时器 return () => clearInterval(timer); }, [increaseNumber]); return ( <div> <h1>{number}</h1> </div> ); }; export default App;
两种方式都能让你得到期望的递增输出,推荐第一种方式,更简洁高效。
内容的提问来源于stack exchange,提问作者najimRizky
相关产品推荐
相关产品推荐

