React计数器中setInterval为何形成闭包?闭包引发计数异常解析
为啥这个React计数器只能跑到2?
先看你的代码:
import React, { useEffect, useState } from "react"; function Counter() { const [count, setCount] = useState(1); useEffect(() => { const id = setInterval( () => { console.log("interval callback triggered"); setCount(count + 1); // This effect depends on the `count` state }, 1000); return () => clearInterval(id); }, []); // 🔴 Bug: `count` is not specified as a dependency return <h1>{count}</h1>; } export default Counter;
先纠正一个误区:箭头函数也会形成闭包
你说()=>{}不会创建闭包是错的——箭头函数和普通函数一样,会捕获它定义时所在作用域的变量,形成闭包。这里的闭包就是setInterval里的那个箭头回调,它捕获了外层useEffect回调作用域里的count变量。
为什么计数器只能到2?
- 组件第一次渲染时,
count是1,此时useEffect执行(依赖是空数组,只跑一次),创建了定时器。这个定时器的回调里,count被闭包钉死在1了。 - 1秒后回调触发,执行
setCount(1+1),count变成2,组件重新渲染。 - 但因为useEffect的依赖是空数组,第二次渲染时useEffect不会重新执行,原来的定时器还在跑——它的回调里的
count还是第一次渲染时的1! - 之后每次定时器触发,都是把
count设置成1+1=2,所以页面上永远显示2,再也涨不上去。
两种修复方法
方法1:把count加入useEffect的依赖数组
每次count变化,useEffect就重新执行,清除旧定时器,创建新的,新回调会捕获最新的count值:
useEffect(() => { const id = setInterval(() => { setCount(count + 1); }, 1000); return () => clearInterval(id); }, [count]); // 加入count作为依赖
方法2:用函数式更新,跳过闭包捕获
直接让React给你传上一次的状态值,不需要依赖当前的count:
useEffect(() => { const id = setInterval(() => { setCount(prevCount => prevCount + 1); // 用函数式更新获取上一次状态 }, 1000); return () => clearInterval(id); }, []); // 不需要依赖count
内容的提问来源于stack exchange,提问作者Nicolas S.Xu
相关产品推荐
相关产品推荐

