How to build a background timer in Expo/React Native

Photo by Djim Loic on Unsplash

Insight #1: use AsyncStorage to store starting time

Nothing’s more persistent than storing things on disk. Let’s use it to store the exact timestamp when the “start” button was pressed. This looks something like this:

const recordStartTime = async () => {
try {
const now = new Date();
await AsyncStorage.setItem("@start_time", now.toISOString());
} catch (err) {
// TODO: handle errors from setItem properly
console.warn(err);
}
};

Insight #2: use AppState to adjust timer

The idea is that when your app is backgrounded, you can assume that the timer isn’t updated. What you want to do though is to bring your timer up-to-date when your app is foregrounded. To do this, we use the AppState API. We will also use the powerful date-fns package to properly calculate difference between two Date objects. This looks something like this:

import { useEffect } from "react";
import { AppState } from "react-native"
import AsyncStorage from "@react-native-community/async-storage";
import { differenceInSeconds } from "date-fns";
const appState = useRef(AppState.currentState);
const [elapsed, setElapsed] = useState(0);
useEffect(() => {
AppState.addEventListener("change", handleAppStateChange);
return () => AppState.removeEventListener("change", handleAppStateChange);
}, []);
const handleAppStateChange = async (nextAppState) => {
if (appState.current.match(/inactive|background/) &&
nextAppState === "active") {
// We just became active again: recalculate elapsed time based
// on what we stored in AsyncStorage when we started.
const elapsed = await getElapsedTime();
// Update the elapsed seconds state
setElapsed(elapsed);
}
appState.current = nextAppState;
};
const getElapsedTime = async () => {
try {
const startTime = await AsyncStorage.getItem("@start_time");
const now = new Date();
return differenceInSeconds(now, Date.parse(startTime));
} catch (err) {
// TODO: handle errors from setItem properly
console.warn(err);
}
};

Warning: a background task this is not

Although this properly updates the view component when the app is brought to the foreground, this definitely does not mean that anything is happening in the background. If you want to do a background task (e.g. fetch data), there is some API support for this. There are even hacky ways to go about it!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Alex Loukissas

Alex Loukissas

Engineer. Optimist. Always building side-projects. #LongLA