React useEffect重新启动计时器

时间:2020-04-09 18:18:28

标签: reactjs react-hooks use-effect

我一直在尝试useEffect挂钩,并且遇到了一个我不知道要解决的问题。

我编写了一个简单的计数器示例,该示例在useEffect中使用setTimeout来更新值。

        if (countState.stopWatch === 0) {
            dispatch({ type: 'reset' });
        }
        else {
            const timeId = setTimeout(() => {
                console.log("Timeout " + JSON.stringify(countState));
                if (countState.stopWatch > 0) {
                    dispatch({ type: 'decrement_stopwatch' });
                }
            }, 1000);

            return () => {
                clearTimeout(timeId);
            };
        }
    });

该代码可以正常运行,但是有一个问题。重新渲染该功能时,它将导致计时器退出并重新设置,这会引起间隔(即,计时器不是每1秒运行一次,而是每隔一秒钟运行一次,以重新渲染该应用程序)。

当我使用基于类的解决方案时,我在类构造函数中运行计时器,并且计时器运行流畅,因为它不会通过重新渲染而取消。

关于在功能版本中如何解决此问题的任何想法?

包括功能和基于类的解决方案在内的完整代码可以在这里找到:

https://github.com/jmc420/react_examples/blob/master/counter/hooks_counter/src/StopWatch.tsx

2 个答案:

答案 0 :(得分:0)

我正尽力想出一种使用setTimeout的方法,就像您在功能组件中尝试做的那样,但是就像您意识到的那样,渲染之间的时间间隔很小并重置超时。

我最好的选择就是使用setInterval。如果您不确定如何将其与钩子一起使用,我将在实验中制作一个快速的Codesandbox:https://codesandbox.io/s/rough-shape-rjmjs

这里的关键是我们只设置一次间隔,只有在卸载时才将其删除,这样它才能始终如一地滴答作响。

对于第一个,我们在set状态调用内执行所有逻辑,因为它本质上可以访问最新状态args,以避免在初始状态下关闭时间间隔的问题,例如:

setCount(c => {
  // c is the most recent count state
  if (c > 0) {
    return c - 1;
  }
  return c;
});

如果那不能满足您的需求,我还添加了第二个,它也使用了引用来绕过关闭问题。我们只是在每个时间间隔刻度时调用分配给ref的函数,但我们会重新分配每个渲染的函数。

timerLogicRef.current = () => {
  if (otherCount > 0) {
    setOtherCount(otherCount - 1);
  }
};

使第二个适合您的异径管设置可能更容易。

答案 1 :(得分:0)

我找到了解决方案。如果使用useEffect时没有依赖项,则您会从react得到警告,但当计时器减少时计时器不会退出。然后,您会遇到引用过期闭包变量的问题,但这可以通过使用useRef来解决。