Обновляет состояние, но для этого использует устаревшее состояние. Переменная state
в вашем обратном вызове setInterval
никогда не изменится после запуска интервала.
Вместо этого используйте форму установки функции обновления состояния, чтобы вы всегда работали с текущим состоянием на тот момент :
let onPressHandler = (): void => {
if(!state.started) {
setState({...state, started: true});
setInterval(()=> {
setState(currentState => {
const newState = {...currentState, secondsLeft: currentState.secondsLeft - 1};
console.log(newState.secondsLeft);
return newState;
});
}, 1000);
}
};
Это более кратко без console.log
:
let onPressHandler = (): void => {
if(!state.started) {
setState({...state, started: true});
setInterval(()=> {
setState(currentState => {...currentState, secondsLeft: currentState.secondsLeft - 1});
}, 1000);
}
};
На отдельном примечании: если у вас есть элементы состояния, которые вы обновляете независимо друг от друга, лучше всего практика заключается в использовании отдельных переменных состояния для них. Кроме того, поскольку они постоянны в вашей функции, лучше всего объявить их как const
. Например:
const [secondsLeft, setSecondsLeft] = useState(25);
const [started, setStarted] = useState(false);
// ...
let onPressHandler = (): void => {
if(!started) {
setStarted(true);
setInterval(()=> {
setSecondsLeft(seconds => seconds - 1);
}, 1000);
}
};
Кроме того, поскольку вы не можете полагаться на setInterval
, чтобы быть точным, я предлагаю сохранить ваше время остановки («сейчас» плюс 25 секунд) и пересчитать, сколько секунд оставляется каждый раз:
let onPressHandler = (): void => {
const stopTime = Date.now() + (DURATION * 1000);
setStarted(true);
setSecondsLeft(DURATION);
const timer = setInterval(()=> {
const left = Math.round((stopTime - Date.now()) / 1000);
if (left <= 0) {
clearInterval(timer);
setStarted(false);
} else {
setSecondsLeft(left);
}
}, 1000);
};
Пример Live (с логикой c для остановки):
const {useState} = React;
const Example = () => {
const DURATION = 25; // seconds
const [started, setStarted] = useState(false);
const [secondsLeft, setSecondsLeft] = useState(0);
if (started) {
return <div>Seconds left: {secondsLeft}</div>;
}
let onPressHandler = ()/*: void*/ => {
const stopTime = Date.now() + (DURATION * 1000);
setStarted(true);
setSecondsLeft(DURATION);
const timer = setInterval(()=> {
const left = Math.round((stopTime - Date.now()) / 1000);
if (left <= 0) {
clearInterval(timer);
setStarted(false);
} else {
setSecondsLeft(left);
}
}, 1000);
};
return (
<input
type="button"
onClick={onPressHandler}
value="Start"
/>
);
};
ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>