skip to Main Content

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


  1. Chosen as BEST ANSWER

    Worked it out! Issue was more with the hook I was using to get App state. Here is the solution

    import { AppState, AppStateStatus } from "react-native";
    import { useEffect, useState } from "react";
    
    export default function useAppState() {
      const [prevAppState, setPrevAppState] = useState(AppState.currentState);
      const [appState, setAppState] = useState(AppState.currentState);
    
      useEffect(() => {
        const handleAppStateChange = (nextAppState: AppStateStatus) => {
          setPrevAppState(appState); // Store the previous app state
          setAppState(nextAppState); // Update the current app state
        };
    
        const appStateSubscription = AppState.addEventListener("change", handleAppStateChange);
    
        return () => {
          appStateSubscription.remove();
        };
      }, [appState]);
    
      return { prevAppState, appState };
    }
    

    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.


  2. Check also for inactive state, which is used in iOS to indicate transitioning between foreground & background.

    if (appState.match(/inactive|background/) && nextAppState === 'active') {
    ...
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search