Mobile Apps with React Native and Expo · Mobile Interviews and Capstone
How React Native Works: Threads, the New Architecture and Lifecycle
The concepts behind the most common React Native interview questions: the JavaScript and UI threads, Hermes, the New Architecture (JSI, Fabric, TurboModules), and app states like active, background and inactive.
What you'll build
Clear, confident answers to "How does React Native render?", "What changed with the New Architecture?" and "How do you handle the app going to the background?", plus a lifecycle handler that pauses work and refreshes data correctly.
The idea
Threads. A React Native app has (at least):
- the JavaScript thread, where your React code and business logic run,
- the UI (main) thread, where native views are drawn and touches arrive,
- background threads for layout and native modules.
If the JS thread is blocked, the app can still scroll natively, but taps and JS-driven updates wait. That's why heavy work belongs off the hot path and animations should run on the UI thread (Reanimated, native driver).
Hermes is the JavaScript engine React Native ships with: it compiles JS to bytecode ahead of time, which speeds up startup and reduces memory.
The New Architecture (the default since React Native 0.76) replaced the old asynchronous "bridge" that serialised every message to JSON:
| Piece | What it does |
|---|---|
| JSI (JavaScript Interface) | JS can hold references to native objects and call them directly, synchronously when needed, without JSON serialisation |
| Fabric | The new rendering system: a shared C++ core, synchronous layout when needed, and support for React's concurrent features |
| TurboModules | Native modules loaded lazily and called through JSI with typed interfaces (generated by Codegen) |
App lifecycle comes from AppState:
active: in the foreground and receiving input,background: not visible; the OS may suspend or kill the app at any time,inactive(iOS): in transition, e.g. the app switcher is open or a call is coming in.
When going to the background: save drafts, pause timers, video and polling. When returning to active: refresh stale data, re-check permissions and the session.
The code
import { useEffect, useRef } from "react";
import { AppState, type AppStateStatus } from "react-native";
export function useAppLifecycle({ onForeground, onBackground }: { onForeground: () => void; onBackground: () => void }) {
const current = useRef<AppStateStatus>(AppState.currentState);
useEffect(() => {
const sub = AppState.addEventListener("change", (next) => {
const wasActive = current.current === "active";
if (!wasActive && next === "active") onForeground(); // returned to the app
if (wasActive && next.match(/inactive|background/)) onBackground();
current.current = next;
});
return () => sub.remove();
}, [onForeground, onBackground]);
}
// Usage on a screen:
// useAppLifecycle({
// onForeground: () => queryClient.invalidateQueries(), // refresh stale data
// onBackground: () => saveDraft(), // the OS may kill the app next
// });
Try the logic
Lifecycle bugs come from transitions, not states. This simulation feeds a realistic sequence of state changes through the same rules as the hook: open the app switcher, come back, go to the background, return after a while.
type AppStateStatus = "active" | "inactive" | "background";
function lifecycle(onForeground: (awayMs: number) => void, onBackground: () => void) {
let current: AppStateStatus = "active";
let leftAt = 0;
return (next: AppStateStatus, at: number) => {
const wasActive = current === "active";
if (!wasActive && next === "active") onForeground(at - leftAt);
if (wasActive && next !== "active") { leftAt = at; onBackground(); }
current = next;
};
}
const log: string[] = [];
const change = lifecycle(
(awayMs) => log.push(awayMs > 60_000 ? `foreground after ${awayMs / 1000}s: refresh data, re-check session`
: `foreground after ${awayMs / 1000}s: nothing stale yet`),
() => log.push("leaving: save draft, pause polling and video"),
);
const events: [AppStateStatus, number][] = [
["inactive", 1_000], // iOS: app switcher opened
["active", 4_000], // came straight back
["inactive", 10_000],
["background", 10_500], // went to another app
["active", 310_500], // back five minutes later
];
for (const [state, at] of events) change(state, at);
console.log(log.join("\n"));
Output
leaving: save draft, pause polling and video foreground after 3s: nothing stale yet leaving: save draft, pause polling and video foreground after 300.5s: refresh data, re-check session
Common mistakes
- Saying React Native "runs in a WebView". It renders native views.
- Describing only the old bridge. Know the New Architecture terms: JSI, Fabric, TurboModules, Codegen.
- Treating
inactiveas background. On iOS,inactivehappens for brief interruptions; don't log users out on it. - Assuming the app keeps running in the background. The OS suspends and kills apps; persist anything important when leaving.
- Blocking the JS thread with heavy work and blaming "React Native is slow".
Key points
- JS thread runs your code; the UI thread draws; keep the JS thread free.
- Hermes is the default engine: faster startup, less memory.
- New Architecture: JSI (direct native calls), Fabric (rendering), TurboModules (lazy, typed native modules).
- Use
AppStatetransitions to save on leave and refresh on return.
Further reading: About the New Architecture, AppState, Hermes.
Practice questions
What is the React Native New Architecture?
Answer
The replacement for the old asynchronous JSON bridge, default since React Native 0.76. JSI lets JavaScript hold references to and call native objects directly; Fabric is the new renderer with a shared C++ core and support for concurrent React; TurboModules are lazily loaded native modules with typed interfaces generated by Codegen.
What should an app do when it moves to the background and when it returns?
Answer
On leaving (inactive → background): save drafts and important state, pause timers, polling, video and location updates, because the OS may suspend or kill the app. On returning to active: refresh stale data, re-check the session and permissions, and resume paused work.
Open in the app to mark lessons complete, take notes and track your progress.