I am building a React Native app and am trying to create a timer. The timer runs normally in the foreground using setInterval and updating a bit of state that holds the amount of seconds and decreases it every one second. When the app is backgrounded the date is captured and stored. When the active is made active again I am trying to calculate the difference between the two dates and then updating the state to reflect the difference so it appears to the user that the timer is running in the background.
I am running into an issue where the state is not updated when the timer is running.
const home = () => {
const WORK_SECONDS = 1500; //1500
const SHORT_REST_SECONDS = 300; //300
const LONG_REST_SECONDS = 900; //900
const ACTIVE_COLOR = '#009688';
const PAUSED_COLOR = '#00695C';
const ADD_TIME = 60;
const NOTIFICATION_TITLE = "Time's up!";
const NOTIFICATION_BODY = "Finished!";
const [timer, setTimer] = useState({seconds: WORK_SECONDS, timeCap: WORK_SECONDS });
const [isActive, setIsActive] = useState(false);
const [timerColor, setTimerColor] = useState(ACTIVE_COLOR);
const [sessionCycle, setSessionCycle] = useState([]);
let time = 0;
const toggle = () => {
if (sessionCycle.length == 8) setSessionCycle([]);
setIsActive(!isActive);
setTimerColor((timerColor == ACTIVE_COLOR && timer.seconds != timer.timeCap) ? PAUSED_COLOR : ACTIVE_COLOR);
}
const reset = () => {
setTimer({...timer, seconds: timer.timeCap});
setIsActive(false);
}
useEffect(() => {
AppState.addEventListener('change', handleChange);
return () => {
AppState.removeEventListener('change', handleChange);
}
}, []);
const handleChange = (newState) => {
if (newState === "active") {
let resumeTime = moment(new Date());
let exitTime = moment(time);
let duration = moment.duration(resumeTime.diff(exitTime, 'seconds'));
let diff = duration.asSeconds() * 1000;
console.log("Attempting to substract - " + diff);
console.log(timer.seconds + " from appstate");
setTimer({...timer, seconds: timer.seconds - diff})
console.log(timer.seconds + " after subtraction");
}
else if (newState == "background") {
time = new Date();
}
}
useEffect(() => {
let interval = null;
if (isActive) {
if (timer.seconds == 0) {
//sendNotification();
// TODO: Add +1 to pomo total after finishing work session
updateSessionCycle("forward");
clearInterval(interval);
toggle();
}
interval = setInterval(() => {
console.log(timer.seconds);
setTimer({...timer, seconds: timer.seconds - 1});
}, 1000);
} else if (!isActive && timer.seconds !== 0) {
clearInterval(interval);
}
return () => clearInterval(interval);
}, [isActive, timer.seconds]);
const addTime = () => {
if (timer.seconds > timer.timeCap - 60) {
setTimer({...timer, seconds: timer.timeCap})
} else {
setTimer({...timer, seconds: timer.seconds + ADD_TIME});
}
}
return (
<View style={styles.chart}>
<ProgressCircle
percent={ (timer.seconds / timer.timeCap) * 100 }
radius={Math.ceil(Dimensions.get('window').height / 6)}
borderWidth={Math.ceil(Dimensions.get('window').height / 30)}
color={timerColor}
shadowColor="#212121"
bgColor="#303030"
>
<Text style={{ color: '#FFF', fontSize: 50 }}>{moment("2015-01-01").startOf('day').seconds(timer.seconds).format('m:ss')}</Text>
<Text style={{ color: '#9E9E9E', fontSize: 20 }}>{(timer.timeCap == WORK_SECONDS) ? "Work Session" : "Break"}</Text>
</ProgressCircle>
</View>
);
}
I suspect your problem is you could be using stale state in your call to setTimer
. In your useEffect
, you are creating a setInterval
function that references timer
in your function closure. Each time that setInterval
function is called, you'll be getting the same timer
variable, so the call to:
setTimer({...timer, seconds: timer.seconds - 1});
...is going to continuously set seconds
to 1 second less than the value of timer.seconds
when you created the interval.
Luckily there's an easy workaround:
setTimer(timer => ({...timer, seconds: timer.seconds - 1}));
This will fetch the latest timer
state each time before performing the update.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.