react-timer-hook
react-timer-hook copied to clipboard
restart Function not working as Expected
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.