为什么将变量列为依赖项 "solve" 是陈旧的闭包问题?

Why does listing a variable as a dependency "solve" the stale closure problem?

我正在尝试理解我所看到的关于 React and stale closures 的一般建议。

具体来说,据我了解,术语“陈旧闭包”用于描述组件和 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 的值不会反映在第一个渲染和最终触发的间隔代码之间发生的 WatchCount 渲染上可能发生的更新。

“解决”这个问题的一般建议是在 dependencies 数组中列出您的变量——useEffect

的第二个参数
useEffect(function iWillBeStale() {
  setInterval(function log() {
    console.log(`Count is: ${count}`);
  }, 2000);
}, [count]);

作为一名javascript程序员,我不明白这是如何“解决”问题的。我们在这里所做的就是创建一个包含变量的数组,并将该数组传递给 useEffect 我天真的观点是 log 中的 count 变量的范围仍然是WatchCount 的第一次调用,应该仍然是陈旧的。

我是否遗漏了 javascript 范围的一些细微差别?

还是因为 useEffect 对这些变量所做的事情而“修复”了一些事情?

或者第三件事?

Am I missing some nuance of javascript's scope here?

不,你是对的,创建数组并将其传递给 useEffect 不影响闭包,闭包常量保持其值。

Or does this "fix" things because of something that useEffect is doing with those variables?

是的。每次状态改变时,反应 运行 整个渲染函数,创建一个新的闭包并再次将其传递给 useEffect。当依赖关系改变时,useEffect 重新 运行s 效果函数,它用新的闭包创建一个新的区间。

此外,效果函数返回一个清理函数,运行在组件卸载时或在下次运行启用效果之前(当依赖项发生变化时)。此清理函数调用clearInterval,这意味着陈旧的闭包不会再次执行,并且并发活动间隔的数量不会增加。

不可否认,这个提议的解决方案有一个巨大的错误:每次 count 更改时清除间隔并开始一个新间隔不会导致一个很好的周期性 2s 间隔,两个日志之间的差距可能很大更大 - 日志记录基本上是去抖动的,并且只会 运行 如果在最后 2 秒内没有发生增量。如果不需要,ref 可能是一个更简单的解决方案:

const [count, setCount] = useState(0);
const countRef = useRef(0);
countRef.current = count;

useEffect(function() {
  setInterval(function log() {
    console.log(`Count is: ${countRef.current}`);
  }, 2000);
}, []);