我刚刚开始学习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)
显然不起作用
答案 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>
);
}