useEffect() 在尝试更新状态时导致奇怪的行为
useEffect() causing strange behaviour when trying to update state
我正在制作一个我认为非常简单的程序,它允许您跟踪 men/women 在会议中发言的时间以及他们发言的次数。但是发生了一些奇怪的事情 - 它部署在这里所以你可以看到:https://make-space.netlify.app/
如果您在没有计时器 运行 的情况下点击 +1 增量按钮 ,它可以正常工作并且完美地增加。然而,如果你有定时器 运行 那么它会上升,然后下降,然后在大约一秒内再次上升。
从控制台日志记录到尝试找出问题所在,我很确定这是我用来处理计时器的 useEffect() 函数,但我无法弄清楚如何以不同的方式进行处理。
我正在更新的是“speakerCount”,它在页面的下方呈现没有问题,但是当试图将它传递给其他组件时会导致问题。
本节中:
const incrementSpeakerCount = () => {
difference(minutes, seconds, speakerCount + 1);
setSpeakerCount(speakerCount + 1);
};
如果我输入 console.log(speakerCount)
它不显示更新。
“差异”道具是我从 parent 组件传递的函数,用于获取状态并将其传递下去。
完整代码在此处,但整个内容可在 github 此处获得:https://github.com/gordonmaloney/makespace
import React, { useState, useEffect } from "react";
import { Button } from "@material-ui/core";
const Timer = ({ gender, difference }) => {
const [seconds, setSeconds] = useState(0);
const [minutes, setMinutes] = useState(0);
const [isActive, setIsActive] = useState(false);
const [speakerCount, setSpeakerCount] = useState(0);
let speakerCountUpdated = speakerCount
function toggle() {
setIsActive(!isActive);
}
function reset() {
difference(0, 0, 0);
setSeconds(0);
setMinutes(0);
setSpeakerCount(0);
setIsActive(false);
}
if (seconds === 60) {
setSeconds(0);
setMinutes(minutes + 1);
}
useEffect(() => {
let interval = null;
if (isActive) {
interval = setInterval(() => {
setSeconds((seconds) => seconds + 1);
difference(minutes, seconds, speakerCount)
}, 1000);
} else if (!isActive && seconds !== 0) {
clearInterval(interval);
}
return () => clearInterval(interval);
}, [isActive, seconds]);
const incrementSpeakerCount = () => {
difference(minutes, seconds, speakerCount + 1);
setSpeakerCount(speakerCount + 1);
};
return (
<div>
<center>
<div className={"timerContainer"}>
<div>
<b>{gender}</b> have been speaking for:
<br />
{minutes} {minutes === 1 ? "minute" : "minutes"} and {seconds}{" "}
{seconds === 1 ? "second" : "seconds"}
</div>
<div>
<Button
className={`button button-primary button-primary-${
isActive ? "active" : "inactive"
}`}
variant="contained"
color="primary"
size="small"
onClick={toggle}
>
{isActive ? "Pause" : "Start"}
</Button>
<br />
<br />
<b>
{speakerCount === 0 ? "No" : speakerCount}{" "}
{speakerCount !== 1
? gender.toLowerCase()
: gender === "Men"
? "man"
: "woman"}
</b>{" "}
{speakerCount !== 1 ? "have" : "has"} spoken.
<br />
<Button
className="button"
variant="contained"
color="primary"
size="small"
onClick={incrementSpeakerCount}
>
+ 1
</Button>
<br />
<br />
<Button
className="button"
variant="contained"
size="small"
onClick={reset}
>
Reset
</Button>
</div>
</div>
</center>
</div>
);
};
export default Timer;
非常欢迎任何想法 - 我对生命周期方法比较陌生,所以怀疑我可能做错了一些非常简单的事情。
非常感谢!
这是一个有趣的问题。您的计时器每秒被调用一次。但是缺少 speakerCount
的依赖项。因此理论上 speakerCount 可能由于 setInterval
.
而滞后
想象一下时间从 0 到 1,在那段时间里,您单击了按钮,但 speakerCount
仍保留当前渲染中的先前值。请记住,如果没有渲染,speakerCount
不能持有新值。
useEffect(() => {
let interval = null;
if (isActive) {
interval = setInterval(() => {
setSeconds((seconds) => seconds + 1);
difference(minutes, seconds, speakerCount)
}, 1000);
} else if (!isActive && seconds !== 0) {
clearInterval(interval);
}
return () => clearInterval(interval);
}, [isActive, seconds, speakerCount]);
如果上面的方法有效,请尝试,在你的情况下你实际上不能准确地得到 1s,因为每次当 isActive
、seconds
或 speakerCount
改变你试图拍摄到再过一秒钟。所以不知何故,即使它在工作,也不能正好是 1 秒。
我正在制作一个我认为非常简单的程序,它允许您跟踪 men/women 在会议中发言的时间以及他们发言的次数。但是发生了一些奇怪的事情 - 它部署在这里所以你可以看到:https://make-space.netlify.app/ 如果您在没有计时器 运行 的情况下点击 +1 增量按钮 ,它可以正常工作并且完美地增加。然而,如果你有定时器 运行 那么它会上升,然后下降,然后在大约一秒内再次上升。
从控制台日志记录到尝试找出问题所在,我很确定这是我用来处理计时器的 useEffect() 函数,但我无法弄清楚如何以不同的方式进行处理。
我正在更新的是“speakerCount”,它在页面的下方呈现没有问题,但是当试图将它传递给其他组件时会导致问题。
本节中:
const incrementSpeakerCount = () => {
difference(minutes, seconds, speakerCount + 1);
setSpeakerCount(speakerCount + 1);
};
如果我输入 console.log(speakerCount)
它不显示更新。
“差异”道具是我从 parent 组件传递的函数,用于获取状态并将其传递下去。
完整代码在此处,但整个内容可在 github 此处获得:https://github.com/gordonmaloney/makespace
import React, { useState, useEffect } from "react";
import { Button } from "@material-ui/core";
const Timer = ({ gender, difference }) => {
const [seconds, setSeconds] = useState(0);
const [minutes, setMinutes] = useState(0);
const [isActive, setIsActive] = useState(false);
const [speakerCount, setSpeakerCount] = useState(0);
let speakerCountUpdated = speakerCount
function toggle() {
setIsActive(!isActive);
}
function reset() {
difference(0, 0, 0);
setSeconds(0);
setMinutes(0);
setSpeakerCount(0);
setIsActive(false);
}
if (seconds === 60) {
setSeconds(0);
setMinutes(minutes + 1);
}
useEffect(() => {
let interval = null;
if (isActive) {
interval = setInterval(() => {
setSeconds((seconds) => seconds + 1);
difference(minutes, seconds, speakerCount)
}, 1000);
} else if (!isActive && seconds !== 0) {
clearInterval(interval);
}
return () => clearInterval(interval);
}, [isActive, seconds]);
const incrementSpeakerCount = () => {
difference(minutes, seconds, speakerCount + 1);
setSpeakerCount(speakerCount + 1);
};
return (
<div>
<center>
<div className={"timerContainer"}>
<div>
<b>{gender}</b> have been speaking for:
<br />
{minutes} {minutes === 1 ? "minute" : "minutes"} and {seconds}{" "}
{seconds === 1 ? "second" : "seconds"}
</div>
<div>
<Button
className={`button button-primary button-primary-${
isActive ? "active" : "inactive"
}`}
variant="contained"
color="primary"
size="small"
onClick={toggle}
>
{isActive ? "Pause" : "Start"}
</Button>
<br />
<br />
<b>
{speakerCount === 0 ? "No" : speakerCount}{" "}
{speakerCount !== 1
? gender.toLowerCase()
: gender === "Men"
? "man"
: "woman"}
</b>{" "}
{speakerCount !== 1 ? "have" : "has"} spoken.
<br />
<Button
className="button"
variant="contained"
color="primary"
size="small"
onClick={incrementSpeakerCount}
>
+ 1
</Button>
<br />
<br />
<Button
className="button"
variant="contained"
size="small"
onClick={reset}
>
Reset
</Button>
</div>
</div>
</center>
</div>
);
};
export default Timer;
非常欢迎任何想法 - 我对生命周期方法比较陌生,所以怀疑我可能做错了一些非常简单的事情。
非常感谢!
这是一个有趣的问题。您的计时器每秒被调用一次。但是缺少 speakerCount
的依赖项。因此理论上 speakerCount 可能由于 setInterval
.
想象一下时间从 0 到 1,在那段时间里,您单击了按钮,但 speakerCount
仍保留当前渲染中的先前值。请记住,如果没有渲染,speakerCount
不能持有新值。
useEffect(() => {
let interval = null;
if (isActive) {
interval = setInterval(() => {
setSeconds((seconds) => seconds + 1);
difference(minutes, seconds, speakerCount)
}, 1000);
} else if (!isActive && seconds !== 0) {
clearInterval(interval);
}
return () => clearInterval(interval);
}, [isActive, seconds, speakerCount]);
如果上面的方法有效,请尝试,在你的情况下你实际上不能准确地得到 1s,因为每次当 isActive
、seconds
或 speakerCount
改变你试图拍摄到再过一秒钟。所以不知何故,即使它在工作,也不能正好是 1 秒。