Javascript Closure with setInterval() in React Hook



在下面的示例中,我希望计数值在控制台中会增加,就像在 UI 中一样。但是,它不是,值始终为 0,但 UI 中的值会增加。我知道这个问题可能与Javascript关闭有关。在我的理解中,count应该指的是useState中的值。由于countsetCount更新,因此该值不断增加。但是,为什么它在日志中没有增加? 演示:https://codesandbox.io/s/gallant-fermat-42lxq?file=/src/App.js

export default function App() {
const [count, setCount] = useState(0);
function innerFunction() {
return () => {
console.log("count: ", count);
setCount(c => c + 1);
};
}
useEffect(() => {
const id = setInterval(innerFunction(), 1000);
return () => clearInterval(id);
}, []);
return <h1>{count}</h1>;
}

问题是在 setInterval 回调中,count 的值不会改变,因为我们创建了一个闭包,其中 count 的值设置为 0,就像效果回调运行时一样

。在第一次渲染时,闭包 log(( 将计数变量捕获为 0。后来,即使计数增加,log(( 仍然使用初始渲染中的 count 作为 0。log(( 是一个过时的闭包。

解决方案是让 useEffect(( 知道闭包日志 (( 取决于计数并正确处理间隔的重置:

在使用中将计数添加为依赖项效果

useEffect(() => {
const id = setInterval(innerFunction(), 1000);
return () => clearInterval(id);
}, [count]);

正确设置依赖项后,useEffect(( 会在计数更改时立即更新闭包。

解释

在没有依赖关系的情况下,useEffect(( 最初只被调用。它不会被重新召回。

useEffect(() => {
console.log("is re-rendering");
const id = setInterval(innerFunction(), 1000);
return () => clearInterval(id);
}, []);

在这里,">正在重新渲染"只记录一次。

如果您使用此语法来设置状态而不是函数语法(获取最新的计数状态(。它还将行为显示为日志作为过时的关闭

setCount( count + 1); //always 1 as it gets 0 as count value

由于useEffect计数更改后不调用,因此它们采用相同的初始输入并提供相同的初始输出,因为它们不知道更改。

如果我们将计数添加为依赖项,则每当计数值更改时都会重新调用useEffect,并且setInterval也会使用新的更新输入重新初始化。

更多信息

https://dmitripavlutin.com/react-hooks-stale-closures/

https://reactjs.org/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often

我已经通过简单的例子展示了类似的问题。

在这里,我在第一次调用 outerfn 后更改了 outerVar 的值(让我们认为它就像一个计数状态(。

但其更新的更改并未反映出来。这就是闭合的工作方式。

let outerVar='initial-outer'
function outerfn(outerVar){
return function innerfn(innerVar){
console.log('outer variable: '+ outerVar)
console.log('inner variable: '+ innerVar)
}
}

let newfn=outerfn(outerVar)
outerVar="updated-outer" // outerVal is now changed
newfn('inside') // but initial outerVal is printed here
newfn=outerfn(outerVar)//resetting closure with updated value (like adding dependency to useEffect)
newfn('inside') // updated outerVal is printed now

最新更新