javascript 为什么将变量列为依赖项可以“解决”陈旧闭包问题?

5f0d552i  于 2023-01-29  发布在  Java
关注(0)|答案(1)|浏览(104)

我试图理解我所看到的关于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对这些变量所做的一些事情而“修复”了一些事情?
或者第三件事?

tpxzln5u

tpxzln5u1#

我是不是漏掉了javascript作用域的一些细微差别?
不,你是对的,创建数组并将其传递给useEffect并不影响闭包,闭包后的常量保持其值。
或者这是因为useEffect对这些变量所做的一些事情而"修复"了一些事情?
是的。每次状态改变时,React都会运行整个呈现函数,这会创建一个新的闭包,并将其再次传递给useEffect。当依赖关系改变时,useEffect会重新运行效果函数,这会使用新的闭包创建一个新的间隔。
另外,作者的解决方案中的效果函数返回了一个清理函数,该函数在组件卸载时或下次运行效果之前(当依赖项发生变化时)运行,该清理函数调用clearInterval,这意味着过时的闭包不会再次执行,同时活动的间隔数不会增加。
诚然,这个提议的解决方案有一个巨大的缺陷:每次count发生变化时清除间隔并启动新的间隔不会产生良好的周期性2s间隔,两个日志之间的间隙可能会大得多-日志记录本质上是去抖动的,并且仅在最后2s内没有发生增量时才会运行。如果不需要这样做,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);
}, []);

相关问题