amrlabib / react-timer-hook

React timer hook

Home Page:https://www.npmjs.com/package/react-timer-hook

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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}

);
};

commented

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.