简体   繁体   English

Javascript 在 React Hook 中使用 setInterval() 关闭

[英]Javascript Closure with setInterval() in React Hook

In the following example, I expect the count value will increase in console as in UI.在以下示例中,我希望控制台中的计数值会像 UI 中一样增加。 However, it doesn't, the value is always 0, but the value increase in UI.然而事实并非如此,该值始终为 0,但 UI 中的值会增加。 I know the issue might be related to Javascript Closure.我知道这个问题可能与 Javascript 关闭有关。 In my understanding, the count should refer to the value in useState .在我的理解中, count应该是指useState中的值。 Since the count is updated by setCount , the value keeps increase.由于countsetCount更新,因此值不断增加。 However, why it doesn't increase in the log?但是,为什么它不会在日志中增加? Demo: https://codesandbox.io/s/gallant-fermat-42lxq?file=/src/App.js演示: 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>;
}

The problem is that inside the setInterval callback, the value of count does not change, because we've created a closure with the value of count set to 0 as it was when the effect callback ran问题是在 setInterval 回调中,count 的值没有改变,因为我们创建了一个闭包,将 count 的值设置为 0,就像效果回调运行时一样

At first render, the closure log() captures count variable as 0. Later, even if count increases, log() still uses count as 0 from initial render.在第一次渲染时,闭包 log() 将 count 变量捕获为 0。后来,即使 count 增加,log() 仍然使用初始渲染中的 count 作为 0。 log() is a stale closure. log() 是一个陈旧的闭包。

The solution is to let know useEffect() that the closure log() depends on count and properly handle the reset of interval:解决方案是让 useEffect() 知道闭包 log() 依赖于 count 并正确处理间隔的重置:

Add count as dependency in useEffect在 useEffect 中添加计数作为依赖项

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

With the dependencies properly set, useEffect() updates the closure as soon as count changes.正确设置依赖项后, useEffect() 会在 count 更改后立即更新闭包。

Explanation解释

In the absence of dependencies , useEffect() is called only initially.在没有依赖项的情况下,仅在最初调用 useEffect() 。 It will not be re-called.它不会被重新调用。

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

Here, " is re-rendering " is logged only once.在这里,“正在重新渲染”只记录一次。

If you use this syntax for setting state instead of functional syntax(which fetch latest count state).如果您使用此语法设置 state 而不是功能语法(获取最新计数状态)。 It also shows behavior as log as stale closure它还将行为显示为过时关闭的日志

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

Since useEffect is not called after count changes, they take same initial input and give same initial output as they doesnot know about a changes.由于在计数更改后不会调用useEffect ,因此它们采用相同的初始输入并给出相同的初始 output,因为它们不知道更改。

If we add count as dependency then useEffect is re-called whenever count value changes and setInterval is also re-initialized with new updated input.如果我们添加 count 作为依赖项,那么只要count值发生变化,就会重新调用useEffect ,并且setInterval也会使用新的更新输入重新初始化。

More Info更多信息

https://dmitripavlutin.com/react-hooks-stale-closures/ 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 https://reactjs.org/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often

Example例子

I have shown a similar issue with simple example.我用简单的例子展示了一个类似的问题。

Here,I have changed the value of outerVar(let us consider it is like a count state) after first calling outerfn.在这里,我在第一次调用outerfn之后改变了outerVar的值(让我们认为它就像一个计数状态)。

But its updated changes is not reflected.但其更新的更改并未反映出来。 This is how closure work.这就是闭包的工作方式。

 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM