restart Function not working as Expected
prpercival opened this issue · comments
If I call restart after the timer has already expired, the timer updates to the new Date but it doesn't start counting down.
Simple Example:
const [hasTimeExpired, setHasTimeExpired] = useState(false);
let dt = new Date();
const {
seconds,
minutes,
hours,
days,
isRunning,
start,
pause,
resume,
restart,
} = useTimer({ expiryTimestamp: dt, onExpire: () => setHasTimeExpired(true), autoStart: false });
useEffect(() => {
let dt = new Date();
dt.setSeconds(dt.getSeconds() + 15);
restart(dt, true);
}, [])
useEffect(() => {
let dt = new Date();
dt.setSeconds(dt.getSeconds() + 120);
restart(dt, true);
}, [hasTimeExpired])
return (
<div className= "Clock-Wrapper">
<span className="Clock-Time-Front">
{hours}:{minutes}:{seconds}
</span>
</div>
)
Hi @prpercival, did you make any progress with this? I have the same issue and want to restart the timer after it has expired.
Interestingly, the 'restart' button in the official demo site does reset+start the 10 minute timer after first expiry. Also, the onExpire does not seem to be doing anything strange.
@erikcvisser After playing around with it, I found that the timer will only restart if you re-render the component your timer is in when updating the time. Not an ideal solution, but it enabled me to reset the timer.
use setTimeout()
setTimeout(() => {
restart((new Date().setSeconds(new Date().getSeconds() + 50)));
}, 1000)
@sjwen98 easy but effective - thanks!
one more solution (when restart func called outside of onExpire callback it works fine):
...
const { seconds, minutes, isRunning, restart } = useTimer({
expiryTimestamp: time,
});
if (isRunning === false) {
const time1 = new Date();
time1.setSeconds(time1.getSeconds() + 1800); // 30 minutes timer
restart(time1);
}
...
Any hope of this getting patched sometime soon? While there are ways to hack around this its frustrating to have to do it to begin with.
Hello, I solved this problem like this:
export const TimerSixtySecond = ({ expiryTimestamp }) => {
const onExpire = async () => {
const time = await new Date();
time.setSeconds(time.getSeconds() + 60);
restart(time, true);
resume;
};
const {
seconds,
minutes,
hours,
days,
isRunning,
start,
pause,
resume,
restart,
} = useTimer({
expiryTimestamp,
onExpire: () => onExpire(),
autoStart: true,
});
return (
<Text Text color="red.500" fontWeight="bold" fontSize={["5xl", "8xl"]}>
{minutes}:{seconds}
);
};
As @prpercival said, it's only going to restart the timer if the component is re-rendered.
An easy workaround then is to flip a boolean in state in the onExpire (causing a re-render), and have a useEffect restart the timer when that piece of state changes. This will restart the timer on a loop, but you can add logic to the useEffect & onExpire callback if you want to conditionally restart:
const Timer = ({ expiryTimeStamp }) => {
const [flipState, setFlipState] = useState(true);
useEffect(() => {
const time = new Date();
time.setSeconds(time.getSeconds() + 10);
restart(time);
}, [flipState])
const {
seconds,
minutes,
hours,
days,
isRunning,
start,
pause,
resume,
restart,
} = useTimer({ expiryTimestamp, onExpire: () => setFlipState(!flipState) });
return (
<div style={{ textAlign: 'center' }}>
<div style={{ fontSize: '50px' }}>
<span>{seconds}</span>
</div>
</div>
);
}
Still happening in 2023, boolean state flip works as a workaround 👍
The setTimeout() mentioned above is an easy work around!
Quite disappointing, despite the workarounds above solving the issue.