In React Native, what happens to the JavaScript runtime, its timers and your in-memory state after the app moves to the background?
answer
- timers ride the native frame clock
- Android: Choreographer callbacks removed on pause
- iOS: suspended soon after leaving
- process death sends no JavaScript event
- save on background, recompute on active
basics
~20 sThe JavaScript runtime stays in memory but stops getting time: React Native pauses its timers, iOS soon suspends the process, and either OS may later kill it without any JavaScript event. Save state when AppState reports background.
solid answer
~40 sNothing is torn down when a React Native app backgrounds; the runtime simply stops running. On Android the timer manager removes its `Choreographer` frame callbacks when the Activity pauses, so `setTimeout` and `setInterval` stop firing unless a Headless JS task is running; on iOS the app is normally suspended shortly after leaving. Timers still pending are not cancelled: they fire late, once the app is active again. Later, either OS may kill the process to reclaim memory, and no `AppState` value or event announces it, so in-memory stores and drafts are simply gone. The fix is to save what must survive when `change` reports `background`, rebuild on every launch, and recompute time-based values from timestamps on return to `active`.
code
tsx · 25 linesimport { useEffect, useRef } from 'react';
import { AppState } from 'react-native';
export function useElapsedSeconds(onTick: (seconds: number) => void) {
const startedAt = useRef(Date.now());
useEffect(() => {
const tick = () =>
onTick(Math.floor((Date.now() - startedAt.current) / 1000));
let id = setInterval(tick, 1000);
const subscription = AppState.addEventListener('change', next => {
clearInterval(id);
if (next === 'active') {
tick();
id = setInterval(tick, 1000);
}
});
return () => {
clearInterval(id);
subscription.remove();
};
}, [onTick]);
}go deeper
Remember that JavaScript does not keep running freely in the background, timers stop firing, and the app can be killed, so anything important is saved when it backgrounds.
Explain the mechanics: React Native timers are driven by native frame callbacks that pause with the Activity, iOS suspends the process, pending timers fire late on resume, and no event announces a kill.
Show the production habits: save on background, restore on every cold start, recompute time from timestamps, drop caches on memoryWarning, and test by killing the process from developer tools.
Frame it as a state-durability contract for the whole app: decide which state must survive process death, where it is stored, and how a cold start rebuilds it.
## The short version When a React Native app leaves the foreground, nothing in JavaScript is torn down at once: the Hermes runtime, your component tree and every in-memory store stay in the process. What changes is that **the runtime stops getting time to run**, and later **the whole process may be killed** without JavaScript being told. Code that works in the foreground breaks in three predictable ways: timers stop ticking, work in flight stalls, and unsaved state disappears. ## What React Native itself does to timers `setTimeout` and `setInterval` in React Native are not browser timers. They are driven by a native timing module that wakes JavaScript on the display's frame clock. - **Android.** The timer manager posts frame callbacks to the `Choreographer`. When the host Activity pauses (the moment `AppState` becomes `background`), it marks itself paused and removes those callbacks, so pending timers stop firing. The one exception built into the timer manager is a running Headless JS task, which keeps timers alive for that task. - **iOS.** The timing module stops its display-link driver when the app enters the background and falls back to a one-shot native timer for the next due callback. That only helps during the short time the process is still running. In both cases a pending timer is **not cancelled**: it stays in the queue and runs, late, once the app is active again. The consequence is that any logic that counts ticks is wrong after a trip to the background. A countdown that subtracts one every second shows too much time left, and a "session idle for 15 minutes" check never fires while the app is away. ## What the operating system does next React Native does not decide what happens after that; the operating system does, and the two platforms differ: | | iOS | Android | |---|---|---| | Shortly after leaving | The app is normally suspended: no code runs at all | The process usually stays alive, but React Native's timers are paused | | Under memory pressure | A suspended app can be terminated without notice | A background process can be killed to reclaim memory | | Exceptions | Declared background modes or a granted background task keep code running | Headless JS tasks and native services keep code running | A process that is killed takes the JavaScript heap with it: component state, a Redux or Zustand store, a half-typed form, the navigation history. The next launch is a cold start. ## There is no "about to be killed" event `AppState` has no terminated value, and Android's native `AppState` module deliberately sends nothing when the host is destroyed. On iOS a suspended app is not woken up to be told it is being terminated. So: 1. **Save on the way out, not at exit.** When `change` reports `background` (or `inactive`, if you want to be early on iOS), write whatever must survive, such as a draft or the current step of a flow, to storage. 2. **Restore on launch.** Treat every start as possibly a cold start after a kill and rebuild from what you saved. 3. **Recompute on the way back.** When `change` reports `active`, derive time-based values from timestamps (`Date.now()` against a stored start or deadline) instead of trusting the number of ticks that ran. `memoryWarning` is a useful early signal on iOS only: it fires when the system issues a memory warning, a moment to drop image caches and large in-memory lists so the app is a less attractive target. Android's `AppState` module does not emit it. ## Pausing your own timers Because timers are paused anyway, stopping them yourself is about correctness and clarity rather than battery. Clear intervals when the app leaves `active`, and on return do one immediate catch-up tick computed from the clock before restarting the interval. The same shape applies to polling loops and animations you drive from JavaScript. ## What this leaf does not cover Getting JavaScript to run while backgrounded (Headless JS, background tasks), reconnecting sockets after a resume, refetching server data on focus and push delivery that depends on app state are separate subjects. The lifecycle facts above are what each of them builds on: in the background, assume nothing runs and anything in memory can vanish.
- Which AppState event gives a React Native app a chance to shed memory before iOS terminates it, and does Android get it too?`memoryWarning`, which fires when iOS issues a system memory warning; it is a cue to drop image caches and large in-memory data. It is iOS-only: Android's native `AppState` module never emits it. It is a hint, not a guarantee, since the OS can still terminate a suspended app afterwards.
- How should a React Native countdown screen show the correct time left after ten minutes in the background?Store an absolute deadline (a timestamp) when the countdown starts, and on every render or tick compute `deadline - Date.now()`. When `AppState` reports `active` again, recompute immediately. Decrementing a counter once per `setInterval` tick is wrong, because the ticks that should have happened in the background never ran.
A backgrounded app is a shop with the shutters down: the stock is still on the shelves, but no cashier is working, and the landlord may clear the premises overnight without knocking. Anything you need tomorrow goes in the safe before closing.
saying these in an interview costs you the question
- setInterval keeps ticking on schedule while a React Native app is in the background.
- AppState reports a terminated state you can use to save data before the app is killed.
- A backgrounded app keeps its JavaScript state until the user swipes it away.
- Background timers are cancelled, so you must re-create them after the app resumes.
- iOS and Android handle a backgrounded React Native app in exactly the same way.