我正在玩React钩子,我正在尝试制作一个非常基本的时钟。它运行得很好,甚至探查器也说没有奇怪的重新发布。让我有点担心的是useEffect调用updateTimeAndDate的地方,它会改变状态。我觉得它应该进入一个重读循环。为什么它仍然有效?对不起,我再好不过了D
它也可以更好一点吗?这是:
const addZero = (trunk) => (trunk >= 10 ? "" : "0") + trunk;
const [hours, setHours] = useState(addZero(0));
const [minutes, setMinutes] = useState(addZero(0));
let timeAndDate;
function updateTimeAndDate() {
timeAndDate = new Date();
setHours(addZero(timeAndDate.getHours()));
setMinutes(addZero(timeAndDate.getMinutes()));
}
useEffect(() => {
updateTimeAndDate();
});
setInterval(updateTimeAndDate, 500);
我在那里放了useEffect,以便在加载页面后立即更新时间,而不是等待很长的半秒。
在显示的代码中会遇到几个问题:
-
您将运行一个无限循环,使组件快速重新渲染。这是因为您在每次渲染时都调用
setInterval
,而通过在setInterval
中调用updateTimeAndDate
,您正在更新状态,这反过来又会导致组件重新渲染。 -
您尚未在
useEffect
中指定依赖数组,因此它将在每次重新渲染时运行,从而进一步加剧了无限循环问题。
一种可能的替代方法是通过指定一个空的依赖数组只调用useEffect
一次。您还可以在组件卸载时清除间隔(通过在useEffect
中指定返回值(。
下面是一个代码沙盒演示:https://codesandbox.io/s/stack-overflow-set-interval-89spq
import React, { useState, useEffect } from "react";
import "./styles.css";
export default function App() {
const addZero = trunk => (trunk >= 10 ? "" : "0") + trunk;
const [hours, setHours] = useState(addZero(0));
const [minutes, setMinutes] = useState(addZero(0));
const [seconds, setSeconds] = useState(addZero(0));
let timeAndDate;
function updateTimeAndDate() {
timeAndDate = new Date();
setHours(addZero(timeAndDate.getHours()));
setMinutes(addZero(timeAndDate.getMinutes()));
setSeconds(addZero(timeAndDate.getSeconds()));
}
useEffect(() => {
const interval = setInterval(updateTimeAndDate, 1000);
return () => clearInterval(interval);
}, []);
// setInterval(updateTimeAndDate, 500);
return (
<div className="App">
<h1>{hours}</h1>
<h2>{minutes}</h2>
<h3>{seconds}</h3>
</div>
);
}
回答您的问题
可以吗?
在useEffect
中调用updateTimeAndDate
(更新状态(是可以的。然而,在您当前的代码中,您将面临@RobertCooper提到的间隔问题。
然而,有一点我不同意罗伯特的观点。这就是你将陷入无限循环。这就引出了您的第二个问题:
我觉得它应该进入重新发布循环。为什么它仍然有效?
由于React处理效果的方式,您当前的代码不会陷入无限循环。
根据文件:
如果将State Hook更新为与当前状态相同的值,React将在不渲染子级或发射效果的情况下退出。
因此,当你用相同的小时和分钟更新状态时,React不会激发你的效果。因此,在这种特殊情况下不存在无限循环。