useEffect 不会更新状态
useEffect won't update state
我正在创建一个简单的番茄应用程序,但卡在了计时器更新逻辑上。
我正在使用 useEffect
更新计时器,但由于某种原因,一秒钟后,计时器停止减少。它似乎没有得到更新,因为控制台日志不断打印初始值。
这是一个可测试的 stackbliz
这是完整的组件:
import './Tomato.css';
import { useState, useEffect } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faPlay, faPause, faArrowsRotate, faArrowDown, faArrowUp } from '@fortawesome/free-solid-svg-icons'
function Tomato() {
const [settings, setSettings] = useState({session: 25, break: 5});
const [timer, setTimer] = useState({minutes: '25', seconds: '00', active: false, isBreak: false});
useEffect(() => {
let timerInterval;
if(timer.active) {
timerInterval = setInterval(() => {
let seconds = parseInt(timer.seconds);
let minutes = parseInt(timer.minutes);
console.log(seconds, ':',minutes)
if(seconds === 0 && minutes === 0) {
// setTimer( {
// minutes: settings.isBreak ? settings.session : settings.break,
// seconds: '00',
// active: true,
// isBreak: !settings.isBreak
// })
// // TODO BIP
} else if(seconds === 0) {
const newTimer = {
...timer,
minutes: timer.minutes-1,
seconds: '59',
}
setTimer( newTimer)
} else {
console.log('in')
seconds = seconds - 1;
// this is not working as expected
setTimer( {
...timer,
seconds: seconds < 10 ? '0'+seconds : seconds+''
})
}
}, 1000);
} else {
clearInterval(timerInterval);
}
return function cleanup() {
clearInterval(timerInterval);
};
}, [timer.active]);
const toggleTimer = (value) => {
setTimer({ ...timer, active: value})
}
const refreshTimer = () => {
setTimer({
minutes: settings.session,
seconds: '00',
active: false,
isBreak: false
})
}
const editSettings = (type, val) => {
if(timer.active) return
const newSettings = { ...settings }
newSettings[type] +=val;
if(newSettings[type] > 60) {
newSettings[type] = 60
}
if(newSettings[type] < 1) {
newSettings[type] = 1
}
setSettings(newSettings)
}
return (
<div className="Tomato">
<h2 className="title">Session</h2>
<div className="session-container">
<div className='session-value'>{timer.minutes}:{timer.seconds}</div>
<div className='controls-container'>
<span className='icon-container'>
<FontAwesomeIcon onClick={()=>{ toggleTimer(true)}} icon={faPlay} />
</span>
<span className='icon-container'>
<FontAwesomeIcon onClick={()=>{ toggleTimer(false)}} icon={faPause} />
</span>
<span className='icon-container'>
<FontAwesomeIcon onClick={()=>{ refreshTimer()}} icon={faArrowsRotate} />
</span>
</div>
</div>
<h2 className="title">Session Length</h2>
<div className="setting-container">
<span className='icon-container' onClick={()=> editSettings('session', -1)}>
<FontAwesomeIcon icon={faArrowDown} />
</span>
<div className="setting-value">{settings.session}</div>
<span className='icon-container' onClick={()=> editSettings('session', 1)}>
<FontAwesomeIcon icon={faArrowUp} />
</span>
</div>
<h2 className="title">Break Length</h2>
<div className="setting-container">
<span className='icon-container' onClick={()=> editSettings('break', -1)}>
<FontAwesomeIcon icon={faArrowDown} />
</span>
<div className="setting-value">{settings.break}</div>
<span className='icon-container' onClick={()=> editSettings('break', +1)}>
<FontAwesomeIcon icon={faArrowUp} />
</span>
</div>
</div>
);
}
export default Tomato;
这是典型的陈旧状态问题,timer
间隔内是陈旧的,因为您没有将其放入效果部门。只需将 deps 数组中的 timer.active
更改为 timer
。
...
return function cleanup() {
clearInterval(timerInterval);
};
}, [timer]);
如果您想了解有关此特定问题的更多信息:
https://overreacted.io/making-setinterval-declarative-with-react-hooks/
我正在创建一个简单的番茄应用程序,但卡在了计时器更新逻辑上。
我正在使用 useEffect
更新计时器,但由于某种原因,一秒钟后,计时器停止减少。它似乎没有得到更新,因为控制台日志不断打印初始值。
这是一个可测试的 stackbliz
这是完整的组件:
import './Tomato.css';
import { useState, useEffect } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faPlay, faPause, faArrowsRotate, faArrowDown, faArrowUp } from '@fortawesome/free-solid-svg-icons'
function Tomato() {
const [settings, setSettings] = useState({session: 25, break: 5});
const [timer, setTimer] = useState({minutes: '25', seconds: '00', active: false, isBreak: false});
useEffect(() => {
let timerInterval;
if(timer.active) {
timerInterval = setInterval(() => {
let seconds = parseInt(timer.seconds);
let minutes = parseInt(timer.minutes);
console.log(seconds, ':',minutes)
if(seconds === 0 && minutes === 0) {
// setTimer( {
// minutes: settings.isBreak ? settings.session : settings.break,
// seconds: '00',
// active: true,
// isBreak: !settings.isBreak
// })
// // TODO BIP
} else if(seconds === 0) {
const newTimer = {
...timer,
minutes: timer.minutes-1,
seconds: '59',
}
setTimer( newTimer)
} else {
console.log('in')
seconds = seconds - 1;
// this is not working as expected
setTimer( {
...timer,
seconds: seconds < 10 ? '0'+seconds : seconds+''
})
}
}, 1000);
} else {
clearInterval(timerInterval);
}
return function cleanup() {
clearInterval(timerInterval);
};
}, [timer.active]);
const toggleTimer = (value) => {
setTimer({ ...timer, active: value})
}
const refreshTimer = () => {
setTimer({
minutes: settings.session,
seconds: '00',
active: false,
isBreak: false
})
}
const editSettings = (type, val) => {
if(timer.active) return
const newSettings = { ...settings }
newSettings[type] +=val;
if(newSettings[type] > 60) {
newSettings[type] = 60
}
if(newSettings[type] < 1) {
newSettings[type] = 1
}
setSettings(newSettings)
}
return (
<div className="Tomato">
<h2 className="title">Session</h2>
<div className="session-container">
<div className='session-value'>{timer.minutes}:{timer.seconds}</div>
<div className='controls-container'>
<span className='icon-container'>
<FontAwesomeIcon onClick={()=>{ toggleTimer(true)}} icon={faPlay} />
</span>
<span className='icon-container'>
<FontAwesomeIcon onClick={()=>{ toggleTimer(false)}} icon={faPause} />
</span>
<span className='icon-container'>
<FontAwesomeIcon onClick={()=>{ refreshTimer()}} icon={faArrowsRotate} />
</span>
</div>
</div>
<h2 className="title">Session Length</h2>
<div className="setting-container">
<span className='icon-container' onClick={()=> editSettings('session', -1)}>
<FontAwesomeIcon icon={faArrowDown} />
</span>
<div className="setting-value">{settings.session}</div>
<span className='icon-container' onClick={()=> editSettings('session', 1)}>
<FontAwesomeIcon icon={faArrowUp} />
</span>
</div>
<h2 className="title">Break Length</h2>
<div className="setting-container">
<span className='icon-container' onClick={()=> editSettings('break', -1)}>
<FontAwesomeIcon icon={faArrowDown} />
</span>
<div className="setting-value">{settings.break}</div>
<span className='icon-container' onClick={()=> editSettings('break', +1)}>
<FontAwesomeIcon icon={faArrowUp} />
</span>
</div>
</div>
);
}
export default Tomato;
这是典型的陈旧状态问题,timer
间隔内是陈旧的,因为您没有将其放入效果部门。只需将 deps 数组中的 timer.active
更改为 timer
。
...
return function cleanup() {
clearInterval(timerInterval);
};
}, [timer]);
如果您想了解有关此特定问题的更多信息: https://overreacted.io/making-setinterval-declarative-with-react-hooks/