I have a ticking clock timer component that I need to reset when the app comes back in from background. I am using expo 49.0.11, react-native 0.72.4. It sucks because this bug is not reproducible in the emulator so I keep needing to try things and then build and release to testflight. What I have below is its use in TickingClockUp, I also have another component TickingClockDown, same problem exists.
I have tried a bunch of things, and what I have now should work but it doesn’t.
import { AppState, AppStateStatus } from "react-native";
import { useEffect, useState } from "react";
export default function useAppState() {
const [appState, setAppState] = useState(AppState.currentState);
useEffect(() => {
const handleAppStateChange = (nextAppState: AppStateStatus) => {
setAppState(nextAppState);
};
const appStateSubscription = AppState.addEventListener("change", handleAppStateChange);
return () => {
appStateSubscription.remove();
};
}, []);
return { appState, nextAppState: AppState.currentState };
}
and then I use this in the actual component like this
import { useEffect, useState } from "react";
import useAppState from "@hooks/useAppState";
import { DateUtils, MoneyUtils } from "@ts-utils";
export default function TickingClockUp({ startTime }: Props) {
const { appState, nextAppState } = useAppState();
const [time, setTime] = useState(DateUtils.getTimeDiff(startTime, new Date()));
useEffect(() => {
const interval = setInterval(() => {
setTime((prevTime) => DateUtils.modifyTime(prevTime, 1));
}, 1000);
return () => {
clearInterval(interval);
};
}, []);
useEffect(() => {
if (appState === "background" && nextAppState === "active") {
setTime(DateUtils.getTimeDiff(startTime, new Date()));
}
}, [appState, nextAppState, startTime]);
const shiftHasntStarted = time.seconds < 0 || time.minutes < 0 || time.hours < 0;
return (
<>
<Text size="heading1" color="blue" weight="bold">
{shiftHasntStarted && "-"}
{time.hours.toString().padStart(2, "0")}:{Math.abs(time.minutes).toString().padStart(2, "0")}:
{Math.abs(time.seconds).toString().padStart(2, "0")}
</Text>
</>
);
}
2
Answers
Worked it out! Issue was more with the hook I was using to get App state. Here is the solution
I needed to check previous and current app state, not next. Then I listen on
prevAppState.match(/inactive|background/) && appState === "active"
(hint from friend above!), and when that is hit inside a useEffect, it means the app is coming back from being inactive and that I neeed to restart my timers.Check also for
inactive
state, which is used in iOS to indicate transitioning between foreground & background.