skip to content

Debugging & Dev Loop

The tools used while building an app: Fast Refresh, the Dev Menu and LogBox, React Native DevTools, source maps for release crashes, and real devices. Interviewers probe how fast you find a bug.

part ofReact Nativeoverview, primer and where to startread it →
on this pageshow

explore

questions

20

Why does a React Native debug build on a USB-connected Android phone need adb reverse tcp:8081 tcp:8081 to reach Metro?

level: juniorimportance: must knowfreq 50%

answer

  1. which host does the phone ask for?
  2. device uses localhost, emulator 10.0.2.2
  3. phone port 8081 forwarded over USB
  4. USB debugging, Android 5.0+
  5. re-run after reconnecting

basics

~20 s

On a physical Android device the debug app asks for the Metro dev server at localhost:8081 on the phone itself. adb reverse forwards the phone's port 8081 over USB to port 8081 on your computer, where Metro listens.

solid answer

~40 s

A debug build loads its JavaScript from the Metro dev server on port 8081. React Native's Android code uses `10.0.2.2` on the stock emulator, which aliases the host machine, but on a physical device it falls back to `localhost`, meaning the phone itself, where nothing is listening. `adb reverse tcp:8081 tcp:8081` tells adb to forward the phone's port 8081 back over the USB cable to the computer's port 8081, so the request reaches Metro, and reloads, Fast Refresh and DevTools all work. The docs recommend it for Android 5.0+ with USB debugging on; with several devices you add `-s <serial>`. The mapping lives with the adb connection, so re-run it after reconnecting. Without a cable, you put both on the same Wi-Fi and set the computer's IP in Dev Settings.

code

bash · 9 lines
bash
# List attached devices and their serials
adb devices

# Forward the phone's port 8081 to the computer's port 8081 (Metro)
adb -s <device serial> reverse tcp:8081 tcp:8081

# Start Metro and run the debug build
npx react-native start
npm run android

go deeper

for a junior

Recall the command, what it forwards, and the prerequisites: USB debugging, a cable, and the device showing as device in adb devices.

for a middle

Explain why the device uses localhost while the emulator uses 10.0.2.2, and when to prefer USB forwarding over the Wi-Fi host setting.

for a senior

Diagnose connection failures quickly: lost mappings after reconnects, several devices without -s, non-default ports, and networks that block Wi-Fi setups.

for a principal

Standardise the device-testing setup for the team so connection problems don't eat into testing time on real hardware.

## What the app is trying to reach A React Native **debug build** does not carry your latest JavaScript; it asks the **Metro dev server** on your computer for the bundle, by default on port **8081**. The question is which address the app uses for "the computer". React Native's Android code picks the dev server host like this: | Where the app runs | Host it uses | Why | |---|---|---| | Stock Android emulator | `10.0.2.2` | The emulator's alias for the host machine's loopback | | Genymotion | `10.0.3.2` | Genymotion's equivalent alias | | Physical device | `localhost` | Nothing better is known, so it assumes the port is forwarded | | Any, if set | The `metro.host` system property | An explicit override | So a phone plugged in over USB asks for `localhost:8081` **on the phone itself**, where nothing is listening. The result is a red screen saying the app could not connect to the dev server. ## What adb reverse does `adb reverse tcp:8081 tcp:8081` tells the Android Debug Bridge to **forward the phone's port 8081 back over the USB connection** to port 8081 on your computer. After that, the app's request to `localhost:8081` reaches Metro. The React Native docs call this the recommended method when: - the device runs Android 5.0 or newer; - **USB debugging** is enabled in Developer options; - the phone is connected to the computer by USB. With several devices attached, target one with `-s`, using the serial from `adb devices`: ```bash adb devices adb -s <device serial> reverse tcp:8081 tcp:8081 ``` The `device` column in `adb devices` must say `device`; `unauthorized` means the phone has not yet accepted the computer's debugging prompt. ## Why it is the recommended route - **No network dependency.** The traffic travels over the USB cable, so office Wi-Fi rules, VPNs and guest networks do not matter. - **Stable address.** `localhost` never changes, whereas your laptop's Wi-Fi IP address does. - **Everything on port 8081 works.** Reloads, Fast Refresh and React Native DevTools all talk to the same dev server port, so one mapping covers them. The mapping belongs to that adb connection, so it can disappear when the cable is unplugged or the adb server restarts. If a device that worked earlier starts showing the connection error after a reconnect, run the command again. If Metro runs on another port, forward that port instead, for example `adb reverse tcp:8082 tcp:8082`, and make sure the app is configured for the same port. ## The alternative: same Wi-Fi network Without a cable, the app can reach Metro over Wi-Fi: 1. Install the app once over USB. 2. Put the phone and the computer on the **same** Wi-Fi network. 3. Open the app; it shows a red connection error at first. 4. Open the Dev Menu, go to **Dev Settings → Debug server host & port for device**, and enter the computer's IP address and port, for example `10.0.1.1:8081`. 5. Choose **Reload JS** from the Dev Menu. This is more fragile: the laptop's IP changes between networks, and many guest or captive-portal networks block device-to-device traffic entirely. ## A quick diagnosis order When a phone shows the connection error, check in this order, cheapest first: 1. Is Metro running on the computer, on the port you expect? 2. Does `adb devices` list the phone as `device`, not `unauthorized` or `offline`? 3. Has `adb reverse` been run for this device since it was last plugged in? 4. Is the app a debug build? A release build never contacts Metro, so a connection error there means something else is wrong. Most "the phone can't see Metro" reports end at step 3. ## Mistakes interviewers listen for - Saying the emulator and the device use the same host; the emulator uses `10.0.2.2`. - Reversing the direction: this is not `adb forward`, which exposes a phone port to the computer. - Assuming the mapping is permanent. - Forgetting that a release build does not need Metro at all, because its JavaScript is bundled into the APK.

  • Why doesn't the Android emulator need adb reverse?
    React Native detects the stock emulator and uses 10.0.2.2, the emulator's alias for the host machine's loopback, so the request reaches Metro on the computer directly. Genymotion gets 10.0.3.2. Only a physical device falls back to localhost, which is why it needs the reverse mapping or a Wi-Fi host setting.
  • Metro runs on port 8082 because 8081 is taken. What changes?
    Forward the port Metro actually uses, adb reverse tcp:8082 tcp:8082, and make sure the app is configured for that same port, since the default is 8081. A mapping for 8081 would forward the phone's 8081 to a port where nothing listens.
  • Does a release build on the same phone need adb reverse?
    No. A release build bundles its JavaScript into the APK and has no Dev Menu or dev server connection, so Metro can be stopped entirely. adb reverse only matters for debug builds that load code from Metro.

adb reverse is call forwarding on the phone: the app dials the phone's own extension 8081, and the forwarding rule, carried over the USB cable, rings the laptop's extension 8081 instead. Unplug the cable and the forwarding rule can be lost.

saying these in an interview costs you the question

  • The phone's localhost is the developer's computer.
  • The emulator and a USB device use the same dev server address.
  • adb forward and adb reverse do the same thing.
  • Once set, the reverse mapping survives unplugging forever.
  • Release builds also need adb reverse to load JavaScript.
open as a page

In React Native, how do you open the Dev Menu on the iOS Simulator, an Android emulator and a device, and what does it offer?

level: juniorimportance: must knowfreq 50%

basics

~20 s

Open the React Native Dev Menu with Ctrl+Cmd+Z on the iOS Simulator, Cmd+M or Ctrl+M on an Android emulator, a shake on a device, adb shell input keyevent 82, or d in Metro. It offers reload, DevTools, Fast Refresh and inspector toggles in development builds only.

open as a page

In a React Native 0.87 app, how do you open React Native DevTools, and what do its main panels let you do?

level: juniorimportance: must knowfreq 62%

basics

~20 s

Press j in the dev server terminal or choose Open DevTools in the Dev Menu. React Native DevTools, a Chrome DevTools-based debugger attached to Hermes on the device, gives you Console, Sources with breakpoints, Network and React Components panels.

open as a page

In React Native, what does Fast Refresh keep when you save an edit, and when does it fall back to a full reload instead?

level: juniorimportance: must knowfreq 60%

basics

~20 s

Fast Refresh swaps edited modules into the running app and keeps function component and Hook state. If an edited module is reached through an import path from outside the React tree, it falls back to a full reload, which re-runs the bundle and loses all state.

open as a page

In a React Native release build, why does a JavaScript crash stack show frames like p@1:132161 instead of your file and line?

level: juniorimportance: must knowfreq 46%

basics

~20 s

A release build runs one Metro bundle compiled to Hermes bytecode, so frames give a function name and a bytecode offset, not your files. Symbolication maps them back with the source map from that exact build, for example using metro-symbolicate.

open as a page

Why isn't the iOS Simulator or Android emulator enough to sign off a React Native feature, and what do they misreport?

level: middleimportance: must knowfreq 55%

basics

~20 s

Simulators run a debug build on fast computer hardware, so speed is wrong both ways, and they lack real cameras, real networks, real touch and vendor variants. Hardware features and performance must be checked on real phones in release builds.

open as a page

What replaced Remote JS Debugging and Flipper in React Native, and why was the old Chrome remote debugger unreliable?

level: middleimportance: must knowfreq 56%

basics

~20 s

React Native DevTools, the default since 0.76. Remote JS Debugging ran the app's JavaScript in desktop Chrome instead of on the device, so behaviour differed and New Architecture modules broke; it was removed in 0.79. Flipper's integration left new projects in 0.74.

open as a page

In a React Native onboarding form, why does typed input survive some Fast Refresh edits but reset on others?

level: middleimportance: must knowfreq 48%

basics

~20 s

Fast Refresh keeps state only for function components and Hooks it can match to the previous version. Class components, files with extra non-component exports, changed Hook arguments or order, a render error and // @refresh reset all cause a remount, which resets the typed input.

open as a page

What does a React Native debug build need to install on a physical iPhone, and how does it then find the Metro dev server?

level: middleimportance: should knowfreq 34%

basics

~20 s

Xcode must sign it with a development Team on the app and Tests targets. At build time the bundling script writes the Mac's IP into the app; the app loads JavaScript from Metro there if reachable, otherwise the bundle embedded at build time.

open as a page

In a legacy React Native app flooded with warnings, what do LogBox.ignoreLogs and LogBox.ignoreAllLogs each hide, and what do they leave visible?

level: middleimportance: should knowfreq 38%

basics

~20 s

LogBox.ignoreLogs drops LogBox entries matching a substring or RegExp, at any level, errors included. LogBox.ignoreAllLogs turns off LogBox notifications, but uncaught errors still open full screen. Neither removes messages from the console, and both do nothing in release builds.

open as a page

In React Native 0.87, how does LogBox treat console warnings, console errors and fatal errors differently during development?

level: middleimportance: should knowfreq 40%

basics

~20 s

LogBox, in development builds only, shows console errors as red notifications you can expand, and fatal or syntax errors as a full-screen view. Warnings are no longer listed in-app: a single banner tells you to open React Native DevTools, where the console holds them.

open as a page

In React Native DevTools, which requests does the Network panel capture, and what are its current gaps?

level: middleimportance: should knowfreq 30%

basics

~20 s

Since 0.83 the Network panel records fetch(), XMLHttpRequest and <Image> requests while DevTools is connected, with headers, timings, previews and an Initiator stack. It does not yet show WebSocket events, mock responses or throttle, and misses custom stacks such as Expo Fetch.

open as a page

During a React Native Fast Refresh update, why does a useEffect with an empty dependency array run again, and how should it be written?

level: middleimportance: should knowfreq 34%

basics

~20 s

During Fast Refresh, Hooks with dependencies always update and their dependency lists are ignored, so even a useEffect with an empty array runs its cleanup and then its body again. Effects should clean up fully and be safe to re-run.

open as a page

How do you get a source map for a React Native release build on Android and on iOS, and which file do you use?

level: middleimportance: should knowfreq 36%

basics

~10 s

Android release builds write one by default because hermesFlags includes -output-source-map; use generated/sourcemaps/react/release/index.android.bundle.map. iOS writes none until you export SOURCEMAP_FILE in Xcode's Bundle React Native code and images build phase.

open as a page

Before shipping a React Native plant-identification app's camera feature, why run a release build on a physical phone, and how do you run one?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Release builds bundle Hermes bytecode, drop dev-only code and the dev server, and may shrink native code, so some bugs and all real performance appear only there. Run one with --mode release (React Native CLI) or expo run with --variant release or --configuration Release.

open as a page

After upgrading a React Native app to 0.82 or later, why do red LogBox errors reading "Uncaught (in promise, id: …)" appear, and how do you handle them?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Since React Native 0.82, unhandled promise rejections are reported as non-fatal errors instead of being silently swallowed, so existing bugs surface as red LogBox errors. Fix each call site with await plus try/catch or .catch(), rather than ignoring the message.

open as a page

In a React Native school-portal app, tapping Sign in shows a generic error and nothing crashes. How would you step through the failed login with React Native DevTools?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Reproduce with DevTools open, read the login request's status and body in the Network panel, jump to the sending code via Initiator, then pause on caught exceptions or a breakpoint and inspect scope, call stack and the form's state in Components.

open as a page

In a React Native app where every save causes a full reload instead of a Fast Refresh, how do you find and fix the cause?

level: seniorimportance: should knowfreq 26%

basics

~20 s

Fast Refresh walks up the edited module's importers looking for component-only modules. It reloads fully when a path reaches the entry file without one, when it meets an import cycle, or when a boundary's exports change. Fix the import graph, not Fast Refresh.

open as a page

A React Native flight-check-in app's release crashes show unreadable JavaScript frames and obfuscated Java frames. How do you make every future crash readable?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Produce and keep every build's symbol files: the composed Hermes source map (iOS needs SOURCEMAP_FILE), R8's mapping.txt for Java frames and the dSYM for iOS native frames, keyed to that exact build, including over-the-air bundles, then verify with a planted crash.

open as a page

In a React Native Hermes release build, why are two source maps composed into one, and what breaks if you skip it?

level: seniorimportance: nice to knowfreq 15%

basics

~20 s

Metro's packager map links the bundle to your sources; hermesc's compiler map links bytecode to the bundle. Hermes crash frames are bytecode positions, so compose-source-maps.js chains the two; without that, frames resolve wrongly or not at all.

open as a page