我试图理解我所看到的关于React and stale closures的一般建议。
具体来说,据我所知,术语“stale闭包”用于描述这样构造组件和useEffect
函数的场景
function WatchCount() {
const [count, setCount] = useState(0);
useEffect(function() {
setInterval(function log() {
console.log(`Count is: ${count}`);
}, 2000);
}, []);
return (
<div>
{count}
<button onClick={() => setCount(count + 1) }>
Increase
</button>
</div>
);
}
React调用WatchCount
来呈现一个组件,并设置count
变量的值,当javascript在两秒钟后调用log
函数时,count
变量将从第一次调用WatchCount
时绑定到count
变量。count
的值将不被绑定。t反映在第一次渲染和最终触发的间隔代码之间发生的WatchCount
渲染上可能已经发生的更新。
解决这个问题的一般建议是在dependencies数组中列出变量--useEffect
的第二个参数
useEffect(function iWillBeStale() {
setInterval(function log() {
console.log(`Count is: ${count}`);
}, 2000);
}, [count]);
作为一个javascript程序员,我不明白这是如何“解决”问题的,我们在这里所做的只是创建一个包含变量的数组,并将该数组传递给useEffect
我天真的看法是,log
中的count
变量仍然作用于WatchCount
的第一次调用,应该仍然是陈旧的。
我是不是漏掉了javascript作用域的一些细微差别?
或者这是因为useEffect
对这些变量所做的一些事情而“修复”了一些事情?
或者第三件事?
1条答案
按热度按时间tpxzln5u1#
我是不是漏掉了javascript作用域的一些细微差别?
不,你是对的,创建数组并将其传递给
useEffect
并不影响闭包,闭包后的常量保持其值。或者这是因为
useEffect
对这些变量所做的一些事情而"修复"了一些事情?是的。每次状态改变时,React都会运行整个呈现函数,这会创建一个新的闭包,并将其再次传递给
useEffect
。当依赖关系改变时,useEffect
会重新运行效果函数,这会使用新的闭包创建一个新的间隔。另外,作者的解决方案中的效果函数返回了一个清理函数,该函数在组件卸载时或下次运行效果之前(当依赖项发生变化时)运行,该清理函数调用
clearInterval
,这意味着过时的闭包不会再次执行,同时活动的间隔数不会增加。诚然,这个提议的解决方案有一个巨大的缺陷:每次
count
发生变化时清除间隔并启动新的间隔不会产生良好的周期性2s间隔,两个日志之间的间隙可能会大得多-日志记录本质上是去抖动的,并且仅在最后2s内没有发生增量时才会运行。如果不需要这样做,ref可能是一个简单得多的解决方案: