I'm building a multiplayer trivia game with React and I've been struggling with building a timer component. The goal would be to implement a time limit per round.
This is the logic behind the feature: https://imgur.com/oJtl98Y
I've copied an implementation online, adding a useEffect to fit my needs. It works by extracting roundDuration from my payload context, then it uses the state variable parsedDeadline to obtain the currentTime by substracting parsedDeadline to Date.now(). I use the special value "-1" to disable the Timer (in case the host sets unlimited time in the game settings).
when 'start_game' or 'start_round' event is caught, the timer is reset to the deadline specified inside the payload (roundDuration is the time limit in milliseconds e.g. 10000).
Timer.tsx
const SECOND = 1000;
const MINUTE = SECOND * 60;
const HOUR = MINUTE * 60;
const DAY = HOUR * 24;
interface TimerProps {
isAnswerDisabled:boolean;
}
export const Timer:React.FC<TimerProps> = ({isAnswerDisabled}) => {
const payload = usePayload()
const info = useGameInfo();
const setInfo = useGameInfoSetter()!;
const parsedDeadline = useMemo(() => new Date(new Date().getTime() + payload.metadata.roundDuration).getDate(), [payload.metadata.roundDuration]);
const [time, setTime] = useState(parsedDeadline - Date.now());
useEffect(() => {
if (time <= 0 && !isAnswerDisabled) {
socket.emit("submit_answer", { room: info.roomId, answer:"" })
setInfo({
...info,
roundDuration: -1
})
return; //timer has reached or passed the deadline, do not update
}
const interval = setInterval(
() => {
const currentTime = parsedDeadline - Date.now();
console.log("currentTime",currentTime)
setTime(currentTime > 0 ? currentTime : 0)
},
1000,
);
return () => clearInterval(interval);
}, [parsedDeadline]);
return (
<div className="timer">
{Object.entries({
Minutes: (time / MINUTE) % 60,
Seconds: (time / SECOND) % 60,
}).map(([label, value]) => (
<div key={label} className="col-4">
<div className="box">
<p>{`${Math.floor(value)}`.padStart(2, "0")}</p>
<span className="text">{label}</span>
</div>
</div>
))}
</div>
);
};
Father Component
{
payload.metadata.isRoundOnGoing
&&
payload.metadata.roundDuration !== -1
&&
info.roundDuration !== -1
&&
<div className='timer'>
<Timer isAnswerDisabled={isAnswerDisabled} />
</div>
}
the problem is that I really don't know how to despawn and respawn this component without breaking the count inside it. I'm pretty new to React and this type of event-driven development so I'm sorry if the solution is simple.
What strategy/pattern should I use to make sure this code above is robust and works correctly?