使用useState Hook引用函数状态时的闭包



此代码来自react文档:

function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
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>;
}

他们说:

依赖项的空集[]意味着该效果在组件装载时只运行一次,而不是在每次重新渲染时运行。问题是,在setInterval回调中,count的值不会改变,因为我们创建了一个闭包,其count的值设置为0,就像运行效果回调时一样。每隔一秒,这个回调就会调用setCount(0+1(,因此计数永远不会超过1。

但是setInterval中的函数不是闭包吗?所以它的计数变量引用了Counter的计数状态,当setCount运行时,count将发生变化,所以setInterval的函数必须能够获得新的计数值?我是不是错过了什么?

但是setInterval中的函数不是闭包吗?所以它的计数变量引用了Counter的计数状态,当setCount运行时,count将发生变化,所以setInterval的函数必须能够获得新的计数值?

不是。闭包关闭单个变量或变量环境。使用React,当组件重新渲染时,整个函数将再次运行,为其中的每个变量创建新的绑定。

例如,Counter第一次运行时:

const [count, setCount] = useState(0);

创建这两个变量。由于useEffect有一个空的依赖数组,它的回调在装载时只运行一次,所以这两个变量是它的闭包将引用的。

但是,一旦状态发生变化,组件重新渲染,函数就会再次运行这一行:

const [count, setCount] = useState(0);

在新的作用域中再次创建这两个新的变量。这个新的count将被递增,但间隔回调不会在它上面结束。

下面是一个在vanillaJS中演示这个问题的实时片段:

let i = 0;
const getRenderValue = () => i++;
const Component = () => {
const thisRenderValue = getRenderValue();
console.log('Rendering with thisRenderValue of', thisRenderValue);
if (thisRenderValue === 0) {
setInterval(() => {
console.log('Interval callback sees:', thisRenderValue);
// Run component again:
Component();
}, 1000);
}
};
Component();

您应该使用prev状态值进行更新。

useEffect(() => {
const id = setInterval(() => {
setCount((prev) => prev + 1);
}, 1000);
return () => clearInterval(id);
}, [count]);

最新更新