Reputation: 1267
My timer stops when I'm locking my screen. The function looks like this so far:
function startTimer(dispatch, getState, duration, tickDuration = 1000) {
return new Promise((resolve, reject) => {
// XXX: Gross local state to keep track of remaining seconds
let remaining = duration;
let handle = setInterval(function() {
const timerState = getState().countdown;
// Clear either if the timer stopped or if the handle is a different
// handle (ex. new timer started before this one completed)
if (!timerState.inProgress || timerState.timerHandle !== handle) {
clearInterval(handle);
resolve(false);
} else {
remaining--;
if (remaining === 0) {
clearInterval(handle);
resolve(true);
}
dispatch({
type: TIMER_COUNTDOWN_TICK,
});
}
}, tickDuration);
dispatch({
type: TIMER_COUNTDOWN_START,
duration: duration,
timerHandle: handle,
});
});
}
Any idea how I can change this, so that it also runs when the user locks the screen and then would come back?
Thanks!
Upvotes: 1
Views: 3130
Reputation: 1267
I solved it with comparing 2 date objects and then getting the seconds from it to correctly update my UI. The suggested react-native-background-timer module didn't work for me. My startTimer function looks now like that:
function startTimer(dispatch, getState, duration, tickDuration = 1000) {
return new Promise((resolve, reject) => {
// XXX: Gross local state to keep track of remaining seconds
let remaining = duration;
var endTime = new Date();
endTime.setSeconds(endTime.getSeconds() + remaining);
let handle = setInterval(function() {
const timerState = getState().countdown;
// Clear either if the timer stopped or if the handle is a different
// handle (ex. new timer started before this one completed)
if (!timerState.inProgress || timerState.timerHandle !== handle) {
clearInterval(handle);
resolve(false);
} else {
var compareTime = new Date();
if (compareTime > endTime) {
clearInterval(handle);
resolve(true);
}
dispatch({
type: TIMER_COUNTDOWN_TICK,
endTime: endTime,
});
}
}, tickDuration);
dispatch({
type: TIMER_COUNTDOWN_START,
duration: duration,
timerHandle: handle,
});
});
}
And then I adjusted the dispatch function as well:
function countdown(state = {
remaining: 0,
inProgress: false,
timerHandle: -1,
}, action) {
switch (action.type) {
case TIMER_COUNTDOWN_START:
return Object.assign({}, state, {
remaining: action.duration,
inProgress: true,
timerHandle: action.timerHandle,
});
case TIMER_COUNTDOWN_TICK:
var endTime = action.endTime;
var compareTime = new Date();
var secondsDifference = Math.abs(endTime - compareTime) / 1000;
return Object.assign({}, state, {
remaining: secondsDifference,
inProgress: true,
});
case REHYDRATE:
const incoming = action.payload.countdown;
if (incoming) {
return {
...state,
...incoming,
remaining: processCountdownRehydration(incoming.remaining),
inProgress: false,
timerHandle: -1,
};
}
return state;
case USER_LOG_OUT:
case BACK_BUTTON_PRESSED:
return { ...state, remaining: 0, inProgress: false, timerHandle: -1 };
default:
return state;
}
}
Upvotes: 1
Reputation: 474
You have to run the timer at background of the app Use this module react-native-background-timer your process is running on background.
import BackgroundTimer from 'react-native-background-timer';
Upvotes: 2
Reputation: 138267
The problem is that intervals are frozen when you leave the tab (e.g. through locking) so you cant guarantee that setInterval always takes a second to run. You may use the clocktime to check instead:
const delay = t => new Promise(res => setTimeout(res, t));
async function timer(duration, tickDuration = 1000){
const end = +new Date() + duration;
while(+new Date() < end) await delay(tickDuration);
}
So one can do
timer(20000).then(console.log)
Upvotes: 1