1
I need a timer component to be reflected in another distant component. To do so, I created a context to save the timers states created, since the later timer (depends on the user’s action) should start synchronized with the source timer.
I have tried several approaches but the derived timer always starts in the standard 5 minute state. It’s like every time I create a timer component React recreates the context/ state instance and it doesn’t make any sense.
In short, the context is responsible for iterating each added timer and setting the changes. The timer component is reflected using parentId.
Follows the code:
TIMER
import React, { useEffect } from 'react';
import { makeStyles } from '@material-ui/core';
import { TimersContext, useTimers } from './context/timer';
import AccessAlarmIcon from '@material-ui/icons/AccessAlarm';
import styles from './styles';
import clsx from 'clsx';
const Timer = ({ parentId, updater, card }) => {
const classes = makeStyles(styles)();
const { handleNewTimer, timers } = useTimers();
const timer = timers.find(t => t.parentId === parentId);
useEffect(() => {
handleNewTimer(parentId, updater);
});
return (
<div className={clsx(classes.container, card && classes.cardStyle)}>
{
card && <span style={{ marginBottom: '3px' }}>
Atualiza em
</span>
}
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<AccessAlarmIcon style={{ marginRight: '3px' }} />
0{timer?.minutes}:{timer?.seconds < 10 ? `0${timer?.seconds}` : timer?.seconds}s
</div>
</div>
)
}
export default Timer;
CONTEXT
import React, { useState, createContext, useContext, useEffect } from 'react';
export const TimersContext = createContext();
export default function TimersProvider({ children }) {
const [timers, setTimers] = useState([]);
useEffect(() => {
let myInterval = setTimeout(() => {
for (var i=0; i < timers.length; i++) {
let timer = timers[i];
if (timer.seconds === 0) {
if (timer.minutes === 0) {
timer.updater();
return;
}
timer.selfUpdate(timer);
setTimers([...timers]);
} else {
timer.setSeconds(timer);
setTimers([...timers]);
}
}
}, 1000)
return () => clearTimeout(myInterval);
}, [timers])
const handleNewTimer = (parentId, updater) => {
let timerExists = timers.find(t => t.parentId === parentId);
if (!timerExists) {
const newTimer = Object.create({
minutes: 5,
seconds: 0,
setSeconds: (timer) => {
timer.seconds = timer.seconds -1; // use this
},
selfUpdate: (timer) => {
timer.seconds = 59;
timer.minutes = timer.minutes -1; // use this
},
updater,
parentId
})
setTimers([...timers.filter(t => t.parentId !== newTimer.parentId), newTimer]);
} else {
console.log('Timer is already running.', timers);
}
}
return (
<TimersContext.Provider value={{ timers, handleNewTimer }}>
{children}
</TimersContext.Provider>
)
}
export function useTimers() {
const context = useContext(TimersContext);
return context;
}