Reactjs setInterval和React挂钩会产生意外的结果
我在我的应用程序中使用create react定义了以下组件:Reactjs setInterval和React挂钩会产生意外的结果,reactjs,react-hooks,Reactjs,React Hooks,我在我的应用程序中使用create react定义了以下组件: import React, { useState } from 'react'; const Play = props => { const [currentSecond, setCurrentSecond] = useState(1); let timer; const setTimer = () => { timer = setInterval(() => { if (curr
import React, { useState } from 'react';
const Play = props => {
const [currentSecond, setCurrentSecond] = useState(1);
let timer;
const setTimer = () => {
timer = setInterval(() => {
if (currentSecond < props.secondsPerRep) {
setCurrentSecond(() => currentSecond + 1);
}
}, 1000);
}
setTimer();
return (
<div>
<div>
<p>{currentSecond}</p>
</div>
</div>
);
}
export default Play;
然后setInterval
回调中的currentSecond
始终返回初始值,即1
非常感谢您的帮助 您的问题是这一行
setCurrentSecond(()=>currentSecond+1)
因为您只调用一次setTimer
,所以在currentSecond
为1的初始状态下,您的间隔将始终关闭
幸运的是,您可以通过传递给setCurrentSecond
的函数中的参数轻松地访问实际的当前状态,如setCurrentSecond(actualCurrentSecond=>actualCurrentSecond+1)
另外,您需要非常小心地任意定义功能组件主体中的间隔,因为它们不会被正确清除,例如,如果再次单击按钮,它将启动另一个间隔,而不会清除上一个间隔
我建议你看看这篇博文,因为它会回答你关于Interval+hooks的任何问题:你的问题是这行
setCurrentSecond(()=>currentSecond+1)
因为您只调用一次setTimer
,所以在currentSecond
为1的初始状态下,您的间隔将始终关闭
幸运的是,您可以通过传递给setCurrentSecond
的函数中的参数轻松地访问实际的当前状态,如setCurrentSecond(actualCurrentSecond=>actualCurrentSecond+1)
另外,您需要非常小心地任意定义功能组件主体中的间隔,因为它们不会被正确清除,例如,如果再次单击按钮,它将启动另一个间隔,而不会清除上一个间隔
我建议你看看这篇博文,因为它会回答你关于Interval+hooks的任何问题:这是因为你的代码在你点击按钮之前正在关闭来自渲染的
currentSecond
值。也就是说javascript不知道重新呈现和挂钩。您确实希望以稍微不同的方式进行设置
import React, { useState, useRef, useEffect } from 'react';
const Play = ({ secondsPerRep }) => {
const secondsPassed = useRef(1)
const [currentSecond, setCurrentSecond] = useState(1);
const [timerStarted, setTimerStarted] = useState(false)
useEffect(() => {
let timer;
if(timerStarted) {
timer = setInterval(() => {
if (secondsPassed.current < secondsPerRep) {
secondsPassed.current =+ 1
setCurrentSecond(secondsPassed.current)
}
}, 1000);
}
return () => void clearInterval(timer)
}, [timerStarted])
return (
<div>
<div>
<button onClick={() => setTimerStarted(!timerStarted)}>
{timerStarted ? Stop : Start}
</button>
<p>{currentSecond}</p>
</div>
</div>
);
}
export default Play;
import React,{useState,useRef,useffect}来自'React';
常量播放=({secondsPerRep})=>{
const secondsPassed=useRef(1)
常数[currentSecond,setCurrentSecond]=useState(1);
常量[timerStarted,setTimerStarted]=useState(false)
useffect(()=>{
让定时器;
如果(计时器开始){
计时器=设置间隔(()=>{
if(第二个经过的电流<第二个经过的电流){
secondsPassed.current=+1
setCurrentSecond(secondsPassed.current)
}
}, 1000);
}
return()=>void clearInterval(计时器)
},[timerStarted])
返回(
setTimerStarted(!timerStarted)}>
{timerStarted?停止:开始}
{currentSecond}
);
}
导出默认播放;
为什么需要ref
和状态?如果您只拥有状态,则每次更新状态时,效果的清理方法都会运行。因此,您不希望您的状态影响您的效果。您可以通过使用ref
计数秒数来实现这一点。对ref的更改不会运行该效果或清除它
但是,您还需要该状态,因为您希望组件在满足条件后重新渲染。但由于状态的更新程序方法(即,setCurrentSecond
)是常量,因此它们也不会影响效果
最后但并非最不重要的一点是,我已将设置间隔与计数逻辑分离。我使用了一个额外的状态在true
和false
之间切换。因此,当您单击按钮时,状态将切换到true
,效果将运行,一切都已设置好。如果要卸载组件,或停止计时器,或secondsPerRep
道具更改,则清除旧间隔并设置新间隔
希望有帮助 这是因为在单击按钮之前,代码正在关闭渲染中的
currentSecond
值。也就是说javascript不知道重新呈现和挂钩。您确实希望以稍微不同的方式进行设置
import React, { useState, useRef, useEffect } from 'react';
const Play = ({ secondsPerRep }) => {
const secondsPassed = useRef(1)
const [currentSecond, setCurrentSecond] = useState(1);
const [timerStarted, setTimerStarted] = useState(false)
useEffect(() => {
let timer;
if(timerStarted) {
timer = setInterval(() => {
if (secondsPassed.current < secondsPerRep) {
secondsPassed.current =+ 1
setCurrentSecond(secondsPassed.current)
}
}, 1000);
}
return () => void clearInterval(timer)
}, [timerStarted])
return (
<div>
<div>
<button onClick={() => setTimerStarted(!timerStarted)}>
{timerStarted ? Stop : Start}
</button>
<p>{currentSecond}</p>
</div>
</div>
);
}
export default Play;
import React,{useState,useRef,useffect}来自'React';
常量播放=({secondsPerRep})=>{
const secondsPassed=useRef(1)
常数[currentSecond,setCurrentSecond]=useState(1);
常量[timerStarted,setTimerStarted]=useState(false)
useffect(()=>{
让定时器;
如果(计时器开始){
计时器=设置间隔(()=>{
if(第二个经过的电流<第二个经过的电流){
secondsPassed.current=+1
setCurrentSecond(secondsPassed.current)
}
}, 1000);
}
return()=>void clearInterval(计时器)
},[timerStarted])
返回(
setTimerStarted(!timerStarted)}>
{timerStarted?停止:开始}
{currentSecond}
);
}
导出默认播放;
为什么需要ref
和状态?如果您只拥有状态,则每次更新状态时,效果的清理方法都会运行。因此,您不希望您的状态影响您的效果。您可以通过使用ref
计数秒数来实现这一点。对ref的更改不会运行该效果或清除它
但是,您还需要该状态,因为您希望组件在满足条件后重新渲染。但由于状态的更新程序方法(即,setCurrentSecond
)是常量,因此它们也不会影响效果
最后但并非最不重要的一点是,我已将设置间隔与计数逻辑分离。我使用了一个额外的状态在true
和false
之间切换。所以当你点击你的
const Play = props => {
const [currentSecond, setCurrentSecond] = useState(1);
const [timer, setTimer] = useState();
const onClick = () => {
setTimer(setInterval(() => {
setCurrentSecond((state) => {
if (state < props.secondsPerRep) {
return state + 1;
}
return state;
});
}, 1000));
}
return (
<div>
<div>
<button onClick={onClick} disabled={timer}>Start</button>
<p>{currentSecond}</p>
</div>
</div>
);
}
const Play = props => {
const [currentSecond, setCurrentSecond] = React.useState(1);
const [isRunning, setIsRunning] = React.useState(false);
useInterval(() => {
if (currentSecond < props.secondsPerRep) {
setCurrentSecond(currentSecond + 1);
}
}, isRunning ? 1000 : null);
return (
<div>
<div>
<button onClick={() => setIsRunning(true)}>Start</button>
<p>{currentSecond}</p>
</div>
</div>
);
}