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

B.J*_*mes 6 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;
Run Code Online (Sandbox Code Playgroud)

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

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

95f*_*973 7

问题:在您当前的实现中,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
Run Code Online (Sandbox Code Playgroud)

如果您只想运行效果并仅将其清理一次(在装载和卸载时),您可以传递一个空数组 ([]) 作为第二个参数。这告诉 React 你的 effect 不依赖于来自 props 或 state 的任何值,所以它永远不需要重新运行。

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

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

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


我的旧答案(不推荐):

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

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

window.interval = setInterval(updateTime, 1000);
Run Code Online (Sandbox Code Playgroud)

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


Arn*_*nas 5

正如 Macro Amorim 回答的那样,useEffect这是最好的方法。这里是代码:

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

    return () => {
        clearInterval(interval);
    }
}, [time])
Run Code Online (Sandbox Code Playgroud)