react 中的 setInterval 不会更新 setState
setInterval in react does not update the setState
我想做一个倒数计时器(25 分钟)。这是 App.js 文件
中的代码
import './App.css';
import React, { useState } from 'react';
function App() {
const [remainingTime, setRemainingTime] = useState(1500);
const [seconds, setSeconds] = useState(0);
const [minute, setMinute] = useState(remainingTime/60);
function timer() {
setRemainingTime(remainingTime-1);
let newMinute = remainingTime/60;
let minuteArray = [...newMinute.toString()];
setMinute(parseInt(minuteArray.slice(0,2).join("")));
setSeconds(remainingTime%60);
console.log(minute);
console.log(seconds);
}
return (
<div className="App">
<div className="pomodoro">
<div className="timer">{minute}:{seconds}</div>
<div className="button-container">
<button className="start" onClick={() => setInterval(timer, 1000)}>start</button>
</div>
</div>
</div>
);
}
export default App;
间隔不更新状态值。分钟值始终为 25,秒值始终为 0。当我不使用 setInterval 而只是像这样使用定时器功能时
<button className="start" onClick={timer}>start</button>
每次我点击开始按钮,值都会改变。任何的想法?我知道我也应该使用 clearInterval 但我不知道把它放在哪里。我应该创建一个包含 setInterval 和 clearInterval 的新函数吗?
在每次重新渲染时,计时器设置为 1500。此处使用闭包,在单击时激活。
function timer() {
let t = remainingTime;
setInterval(() => {
t -= 1;
setRemainingTime(t);
let newMinute = t / 60;
let minuteArray = [...newMinute.toString()];
setMinute(parseInt(minuteArray.slice(0, 2).join("")));
setSeconds(t % 60);
console.log(minute);
console.log(seconds);
}, 1000); }
问题
主要问题是 timer
回调中状态的陈旧外壳。
看来你把它弄得有点复杂了。
解决方案
minute
和 seconds
被认为是派生状态(来自 remainingTime
)所以它不应该也存储在状态中,它很容易从状态中计算出来。
- 使用功能状态更新从先前的状态更新
remainingTime
状态,而不是回调排队的渲染周期的状态。
- 使用 React 引用来保存间隔计时器引用,因此可以清除间隔。
- 使用
useEffect
挂钩 return 清除函数以在组件卸载时清除任何 运行 间隔。
代码:
function App() {
const timerRef = React.useRef();
React.useEffect(() => {
return () => clearInterval(timerRef.current);
}, []);
const [remainingTime, setRemainingTime] = React.useState(1500);
function timer() {
setRemainingTime((remainingTime) => remainingTime - 1);
}
const startTimer = () => {
clearInterval(timerRef.current); // clear any running interval
setRemainingTime(1500); // reset state back to 25 minutes
timerRef.current = setInterval(timer, 1000); // start/restart interval
};
const minute = String(Math.floor(remainingTime / 60)).padStart(2, 0);
const seconds = String(remainingTime % 60).padStart(2, 0);
return (
<div className="App">
<div className="pomodoro">
<div className="timer">
{minute}:{seconds}
</div>
<div className="button-container">
<button className="start" onClick={startTimer}>
start
</button>
</div>
</div>
</div>
);
}
演示
因为这可能是一个 follow-up 问题,所以使用另一个 useEffect
来“监听” remainingTime
状态,当时间达到 0 时,清除间隔,重置 remainingTime
back 1500 (for display), 并显示番茄钟的任何警报或警报或其他内容 break/schedule.
我想做一个倒数计时器(25 分钟)。这是 App.js 文件
中的代码import './App.css';
import React, { useState } from 'react';
function App() {
const [remainingTime, setRemainingTime] = useState(1500);
const [seconds, setSeconds] = useState(0);
const [minute, setMinute] = useState(remainingTime/60);
function timer() {
setRemainingTime(remainingTime-1);
let newMinute = remainingTime/60;
let minuteArray = [...newMinute.toString()];
setMinute(parseInt(minuteArray.slice(0,2).join("")));
setSeconds(remainingTime%60);
console.log(minute);
console.log(seconds);
}
return (
<div className="App">
<div className="pomodoro">
<div className="timer">{minute}:{seconds}</div>
<div className="button-container">
<button className="start" onClick={() => setInterval(timer, 1000)}>start</button>
</div>
</div>
</div>
);
}
export default App;
间隔不更新状态值。分钟值始终为 25,秒值始终为 0。当我不使用 setInterval 而只是像这样使用定时器功能时
<button className="start" onClick={timer}>start</button>
每次我点击开始按钮,值都会改变。任何的想法?我知道我也应该使用 clearInterval 但我不知道把它放在哪里。我应该创建一个包含 setInterval 和 clearInterval 的新函数吗?
在每次重新渲染时,计时器设置为 1500。此处使用闭包,在单击时激活。
function timer() {
let t = remainingTime;
setInterval(() => {
t -= 1;
setRemainingTime(t);
let newMinute = t / 60;
let minuteArray = [...newMinute.toString()];
setMinute(parseInt(minuteArray.slice(0, 2).join("")));
setSeconds(t % 60);
console.log(minute);
console.log(seconds);
}, 1000); }
问题
主要问题是 timer
回调中状态的陈旧外壳。
看来你把它弄得有点复杂了。
解决方案
minute
和seconds
被认为是派生状态(来自remainingTime
)所以它不应该也存储在状态中,它很容易从状态中计算出来。- 使用功能状态更新从先前的状态更新
remainingTime
状态,而不是回调排队的渲染周期的状态。 - 使用 React 引用来保存间隔计时器引用,因此可以清除间隔。
- 使用
useEffect
挂钩 return 清除函数以在组件卸载时清除任何 运行 间隔。
代码:
function App() {
const timerRef = React.useRef();
React.useEffect(() => {
return () => clearInterval(timerRef.current);
}, []);
const [remainingTime, setRemainingTime] = React.useState(1500);
function timer() {
setRemainingTime((remainingTime) => remainingTime - 1);
}
const startTimer = () => {
clearInterval(timerRef.current); // clear any running interval
setRemainingTime(1500); // reset state back to 25 minutes
timerRef.current = setInterval(timer, 1000); // start/restart interval
};
const minute = String(Math.floor(remainingTime / 60)).padStart(2, 0);
const seconds = String(remainingTime % 60).padStart(2, 0);
return (
<div className="App">
<div className="pomodoro">
<div className="timer">
{minute}:{seconds}
</div>
<div className="button-container">
<button className="start" onClick={startTimer}>
start
</button>
</div>
</div>
</div>
);
}
演示
因为这可能是一个 follow-up 问题,所以使用另一个 useEffect
来“监听” remainingTime
状态,当时间达到 0 时,清除间隔,重置 remainingTime
back 1500 (for display), 并显示番茄钟的任何警报或警报或其他内容 break/schedule.