如何在React功能组件中正确设置setInterval计时器?

时间:2020-08-10 03:50:32

标签: javascript reactjs react-component

我刚刚开始学习react,并且正在观看有关状态和挂钩的教程。它只是处理每1000毫秒的更新时间(或者我认为是这样)。

import React from "react";
let count = 0;

function App() {
  const now = new Date().toLocaleTimeString();
  let [time, setTime] = React.useState(now);


  function updateTime(){
    const newTime = new Date().toLocaleTimeString();
    setTime(newTime);
    count++;
    console.log(count);
    console.log(new Date().getMilliseconds());
  }

  setInterval(updateTime, 1000);


  return (
    <div className="container">
      <h1>{time}</h1>
      <button onClick = {updateTime}>time</button>
    </div>
  );
}

export default App;

本教程的目的只是一个有关如何更新时间的简单示例,但是我注意到的是,它每1000毫秒被更新一次(突发)。我怀疑每次更改挂钩都会出现新组件,但是旧组件仍在更新并产生更多组件,从而导致每1000毫秒的调用次数呈指数增长。

我很好奇这里发生了什么?我该如何说一个简单的计数器每1000毫秒更新一次? setTime(count)显然不起作用

4 个答案:

答案 0 :(得分:2)

问题:在您当前的实现中,每次渲染组件时都会调用setInterval(即,在设置时间状态后也会调用),并且会创建新的时间间隔-在控制台中产生了这种“指数增长”。

如评论部分所述:useEffect是在React中处理功能组件时处理此场景的最佳方法。看下面我的例子。 useEffect仅在初始组件渲染后(安装组件时)运行。

React.useEffect(() => {
  console.log(`initializing interval`);
  const interval = setInterval(() => {
    updateTime();
  }, 1000);

  return () => {
    console.log(`clearing interval`);
    clearInterval(interval);
  };
}, []); // has no dependency - this will be called on-component-mount

如果您要运行效果并仅将其清理一次(在挂载和 卸载),则可以传递一个空数组([])作为第二个参数。这个 告诉React你的效果不依赖于道具的任何值 或状态,因此它不需要重新运行。

在您的方案中,这是“空数组作为第二个参数”的完美用法,因为您只需要在安装组件时设置间隔,而在卸载组件时清除间隔。看看useEffect也返回的函数。这是我们的清理功能,将在卸载组件时运行。这将“清理”,或者在这种情况下,清除不再使用组件的时间间隔。

我编写了一个小应用程序,演示了我在此答案中涵盖的所有内容:https://codesandbox.io/s/so-react-useeffect-component-clean-up-rgxm0?file=/src/App.js

我合并了一个小的路由功能,以便可以观察到组件的“卸载”。


我的旧答案(不推荐):

每次重新渲染组件时都会创建一个新的时间间隔,这是您为时间设置新状态时发生的情况。我要做的是在设置新间隔之前清除上一个间隔(clearInterval

try {
  clearInterval(window.interval)
} catch (e) {
  console.log(`interval not initialized yet`);
}

window.interval = setInterval(updateTime, 1000);

https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval

答案 1 :(得分:1)

正如Macro Amorim回答的那样,useEffect是执行此操作的最佳方法。这里的代码:

useEffect(() => {
    const interval = setInterval(() => {
         const newTime = new Date().toLocaleTimeString();
         setTime(newTime);
    }, 1000)

    return () => {
        clearInterval(interval);
    }
}, [time])

答案 2 :(得分:0)

在这种情况下,您可以使用React的useEffect钩子,并且在函数内部的返回位置上可以使用clearInterval函数,建议您进行查找,useEffect非常适合您要执行的操作。 / p>

答案 3 :(得分:0)

你也可以试试这个 -

import React, { useEffect, useState } from 'react';

export default function App() {
  const [timer, setTimer] = useState(0);
  const [toggle, setToggle] = useState(false);

  useEffect(() => {
    let counter;
    if (toggle) {
      counter = setInterval(() => setTimer(timer => timer + 1), 1000);
    }
    return () => {
      clearInterval(counter);
    };
  }, [toggle]);

  const handleStart = () => {
    setToggle(true);
  };

  const handleStop = () => {
    setToggle(false);
  };

  const handleReset = () => {
    setTimer(0);
    setToggle(false);
  };

  return (
    <div>
      <h1>Hello StackBlitz!</h1>
      <p>Current timer - {timer}</p>
      <br />
      <button onClick={handleStart}>Start</button>
      <button onClick={handleReset}>Reset</button>
      <button onClick={handleStop}>Stop</button>
    </div>
  );
}