【问题标题】:React UseEffect Example Not Working ReasonReact UseEffect 示例不工作原因
【发布时间】:2019-11-13 19:35:39
【问题描述】:

为什么这不能作为正常的一秒计数器工作?


function UseEffectBugCounter() {
  const [count, setCount] = React.useState(0);

  React.useEffect(() => {
    const intervalId = setInterval(() => {
      setCount(count + 1);
      console.log(count);
    }, 1000);

    return () => clearInterval(intervalId);
  }, []);

  return <div>The count is: {count}</div>;
}

示例:https://codesandbox.io/s/sparkling-rgb-6ebcp

-是因为陈旧的闭包吗?

-是不是因为计数是一个状态变量,并且组件会在状态更新后重新渲染,所以会创建一个新的间隔来创建某种循环?

-还有别的吗?

如果可能的话,我正在寻找为什么会在这个答案中出现这种情况,有几篇不同的文章说明了它为什么不起作用(如上所述)。但到目前为止,还没有一个能够提供好的论据。

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

    您可以使用回调设置状态以使用最新的计数器值:

    setCount(count => (count + 1));
    

    【讨论】:

      【解决方案2】:

      您可能需要在useEffect 中添加count 的依赖项。目前useEffect 仅在挂载时调用,之后不调用(即当计数值更改时)。

      所以它总是显示0,因为useEffect 只执行一次(在挂载时),并且那个时候计数值设置为0。因此,由于关闭,它每次在setInterval 上记录 0。

      我已更新代码沙箱以查找原因和有意义的日志。这是沙盒链接:https://codesandbox.io/s/admiring-thompson-uz2xe

      如何找到闭包值:您可以查看日志并遍历原型对象找到[[Scopes]],您将获得如下截图所示的值:

      这会起作用:

      React.useEffect(() => {
          const intervalId = setInterval(() => {
            setCount(count + 1);
            console.log(count);
          }, 1000);
      
          return () => clearInterval(intervalId);
        }, [count]);
      

      您可以查看此文档:https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect

      您也可以阅读此内容:您也可以阅读此内容:https://overreacted.io/making-setinterval-declarative-with-react-hooks/

      希望这会有所帮助!

      【讨论】:

      • 通过添加依赖项,您将在每次渲染上重新运行此效果。最好使用 setState 回调
      • 嘿,这可以做到,但如果您在setInterval 中看到日志,它仍然是 0。
      • 我不认为我们关心日志,它只是为了调试。如果您需要这些日志,只需将 console.log 放入 callbask:setCount(count =&gt; { console.log(count); return count + 1; });
      • Hey Andres 我知道这是为了调试,回调方法很好的获取变量的最新值。并且有一个依赖项在每个渲染上创建不同的 setInterval。您可以阅读此博客以获得更好的理解:overreacted.io/making-setinterval-declarative-with-react-hooks
      【解决方案3】:

      如果您删除了 useEffect 的第二个参数,如果您的状态发生了一些变化,您的应用程序将始终呈现,但这是不好的做法。您需要在第二个参数中选择您需要观看的参数...

      选择参数的示例:

      React.useEffect(() => {
          const intervalId = setInterval(() => {
            setCount(count + 1);
            console.log(count);
          }, 1000);
      
          return () => clearInterval(intervalId);
        }[count]);
      

      没有

      React.useEffect(() => {
          const intervalId = setInterval(() => {
            setCount(count + 1);
            console.log(count);
          }, 1000);
      
          return () => clearInterval(intervalId);
        });
      

      【讨论】:

        【解决方案4】:

        因为你没有将count添加到useEffect的依赖中,所以里面的效果计数总是0

        您应该使用useReducer 来解决您的问题:

        function UseEffectBugCounter() {
          const [count, dispatchCount] = React.useReducer((state, { type }) => {
            switch(type) {
              case 'inc':
                return state + 1
              default:
                return state
            }
          }, 0);
        
          React.useEffect(() => {
            const intervalId = setInterval(() => {
              dispatchCount({ type: 'inc'})
            }, 1000);
            return () => clearInterval(intervalId);
          }, []);
          return <div>The count is: {count}</div>;
        }
        

        【讨论】:

          【解决方案5】:

          您需要在useEffect 中传递计数而不是空白数组

          function UseEffectBugCounter() {
            const [count, setCount] = React.useState(0);
            React.useEffect(() => {
              const intervalId = setInterval(() => {
                setCount(count + 1);
                console.log(count);
              }, 1000);
              return () => clearInterval(intervalId);
            },[count]);
            return <div>The count is: {count}</div>;
          }
          

          【讨论】:

          • 嘿 Vahid,这不是最好的方法,这将导致在每次渲染时创建新的 setInterval,您可以通过记录 intervalId 来检查。我最初的想法是一样的,您可以阅读我的答案并查看博客 overreacted.io。
          猜你喜欢
          • 2019-10-29
          • 2016-03-04
          • 1970-01-01
          • 2015-07-01
          • 2020-07-02
          • 1970-01-01
          • 2017-01-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多