I'm using react-timer-hook
package in my next.js project to display a timer like you can see in the screenshot below:
Now the issue is I want to persist this timer elapsed time into the local storage and whenever the page reloads manually then I need the Timer to start from that specific elapsed time that I'm trying to get from the local storage, but whenever I reload the page manually then Timer starts from the initial state's value. below are the codes:
Timer Component
function Timer({ seconds, minutes, hours }) {
return (
<Typography variant="h5" fontWeight={'bold'} component={'div'}>
<span>{String(hours).padStart(2, '0')}</span>:
<span>{String(minutes).padStart(2, '0')}</span>:
<span>{String(seconds).padStart(2, '0')}</span>
</Typography>
);
}
I'm adding 3600 seconds into expiryTimestamp
i.e., current date and time to get one hour of Timer.
let expiryTimestamp = new Date();
expiryTimestamp.setSeconds(expiryTimestamp.getSeconds() + 3600);
Aslo I'm using another state with same 3600 seconds initial value
const [elapsed, setElapsed] = useState(3600);
I'm using useEffect and decrementing the elapsed value on every second into local storage.
useEffect(() => {
const interval = setInterval(() => {
localStorage.setItem('elapsed', JSON.stringify(elapsed--));
}, 1000);
return () => clearInterval(interval);
}, [elapsed]);
Now I'm getting the elapsed value from the local storage
useEffect(() => {
const elapsed = localStorage.getItem('elapsed');
if (elapsed) {
setElapsed(JSON.parse(elapsed));
}
}, []);
Again I'm using another variable to create current date and time + elapsed value
let currentTime = new Date();
currentTime.setSeconds(currentTime.getSeconds() + elapsed);
Finally I'm passing the currentTime
in useTimer
hook
const { seconds, minutes, hours } = useTimer({
expiryTimestamp: currentTime,
onExpire: handleForm,
});
Elapsed time is properly storing in the local storage, but still Timer starts from 3600 seconds.
We can use expiryTimestamp
value of use timer as function to initiate its value. Check the following component
import { useEffect } from 'react';
import { useTimer } from 'react-timer-hook';
export default function Timer() {
const { seconds, minutes, hours } = useTimer({
expiryTimestamp: () => {
/** determine the expiry time stamp value all at once */
const time = new Date(),
elapsedTime = Number(window.localStorage.getItem('elapsed')) || 3600;
time.setSeconds(time.getSeconds() + elapsedTime);
return time;
},
onExpire: () => alert('expired')
});
/** update elapsed value in local storage */
useEffect(() => {
const elapsedSeconds = hours * 60 * 60 + minutes * 60 + seconds;
window.localStorage.setItem('elapsed', elapsedSeconds);
}, [seconds]);
return (
<div>
<span>{String(hours).padStart(2, '0')}</span>:
<span>{String(minutes).padStart(2, '0')}</span>:
<span>{String(seconds).padStart(2, '0')}</span>
</div>
);
}
If you're using this component in next.js. You should use dynamic import with ssr disabled. otherwise you'll receive an error because SSR doesn't recognize the window.localStorage
api. Check below
import dynamic from 'next/dynamic';
import React from 'react';
const Timer = dynamic(() => import('./timer'), { ssr: false });
export default function Index = () => {
return <Timer />;
};