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?
answer
- network evidence before code
- Initiator jumps to the call site
- Pause on caught exceptions
- conditional breakpoints and logpoints
- Console evaluates in the paused frame
basics
~20 sReproduce 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.
solid answer
~50 sI'd open React Native DevTools before reproducing, since requests are recorded while it's connected, then tap Sign in and read the **Network** panel: no request means the handler fails before sending; a 4xx points at the payload; a 5xx HTML page usually means the client's JSON parse throws; a 200 means the bug is in the success path. The **Initiator** tab takes me to the call site. Because a generic message usually means a swallowed error, I'd turn on **Pause on caught exceptions** or set a conditional breakpoint in the `catch`, then read Scope and Call Stack and evaluate expressions in the Console, which runs in the paused frame. In **Components** I'd check the form's state, like a padded school code. If it only fails in a release build, DevTools can't attach, so I'd reproduce in debug first.
code
tsx · 37 linesimport { useState } from 'react';
import { Pressable, Text, TextInput, View } from 'react-native';
type LoginResponse = { token: string };
export function SignInScreen({ onSignedIn }: { onSignedIn: (token: string) => void }) {
const [schoolCode, setSchoolCode] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState<string | null>(null);
async function signIn() {
try {
const res = await fetch('https://portal.example.edu/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ schoolCode, password }),
});
// Bug: res.ok is never checked. A 502 HTML page makes res.json() throw.
const data = (await res.json()) as LoginResponse;
onSignedIn(data.token);
} catch {
// The real error is swallowed here; Pause on caught exceptions stops on the throw.
setError('Something went wrong');
}
}
return (
<View>
<TextInput value={schoolCode} onChangeText={setSchoolCode} placeholder="School code" />
<TextInput value={password} onChangeText={setPassword} secureTextEntry placeholder="Password" />
<Pressable onPress={signIn}>
<Text>Sign in</Text>
</Pressable>
{error ? <Text>{error}</Text> : null}
</View>
);
}go deeper
Know how to set a breakpoint in the Sources panel, read a request in the Network panel, and log values in the Console.
Explain the order of work: network evidence first, then Initiator, breakpoints and Pause on caught exceptions, then state in the Components panel.
Show you can narrow a vague report with evidence: separate server rejection from client parsing, find the swallowed error, test a fix by editing state live, and recognise when a release-only bug needs other tools.
Turn the incident into a guardrail: error handling that keeps the real cause, and client logs that make the next login failure diagnosable without a debugger.
## The setting A school-portal app shows *"Something went wrong"* when a student taps **Sign in**. Nothing crashes, LogBox shows nothing useful, and the backend team says "works for us". The skill being tested is using **React Native DevTools** to go from symptom to cause with evidence rather than guesses. Everything below needs a **debug build** on Hermes with the dev server running, because DevTools is disabled in release builds. ## Step 1: read the evidence in the Network panel Open DevTools (`j` in the dev server terminal or **Open DevTools** in the Dev Menu) **before** reproducing, because requests are recorded while it is connected. Then tap Sign in and read the result: | What the Network panel shows | What it tells you | Next move | |---|---|---| | No login request at all | The handler fails before sending: validation, a missing token, an exception | Breakpoint in the submit handler | | A 4xx response | The server rejected the payload or credentials | Compare the request body with what the API expects | | A 5xx with an HTML body | The server or a gateway failed, and the client probably can't parse the reply | Pause on the parse step | | A 200 response, yet the error still appears | The bug is in the client's handling | Step through the success path | The **Initiator** tab shows the call stack that sent the request, so one click takes you from the request to the line in your code. ## Step 2: stop where the error is swallowed A generic error message usually means a `catch` block turned a real error into a string. Three ways to stop there: - **Pause on caught exceptions** (in the Sources panel's breakpoint options): execution stops at the moment the error is thrown, even though your code or a library catches it. Plain *Pause on exceptions* would miss it, because the error is handled. - **A breakpoint or conditional breakpoint** on the `fetch` line or in the `catch`. Open the file with `Cmd/Ctrl+P`, click the line number, and add a condition so it only fires on the failing path. - **A `debugger;` statement** typed in your editor, which reaches the device through Fast Refresh. A **logpoint** is the non-pausing variant: it prints an expression each time the line runs without editing the file, which helps when pausing would disturb timing. ## Step 3: inspect while paused When execution stops, the app shows a **Paused in Debugger** overlay. The app's JavaScript is halted: no React updates, JavaScript timers or event handlers run until you resume (tapping the overlay also resumes). 1. Read the **Scope** pane: the request body, the response status, the caught error object. 2. Read the **Call Stack** to see which layer turned the error into a generic message. 3. Add **watch expressions** for values you need across steps. 4. Type in the **Console**: while paused it evaluates in the paused frame's scope, so checks such as `res.status` or `res.headers.get('content-type')` run against real values. 5. **Step over / into** to follow the flow until the wrong branch is taken. ## Step 4: check the input side in the Components panel Many login failures are bad input rather than bad networking. In the **Components** panel, use **Select element** and tap the form to find the sign-in screen component, then read its props and state: a school code with trailing whitespace, a stale tenant ID, an empty password field after autofill. You can **edit state live** to test a hypothesis, for example trimming the code, and tap Sign in again without a rebuild. ## Where DevTools stops helping - **Release-only failures.** DevTools, the Dev Menu and LogBox are disabled in release builds. Reproduce against the same backend in a debug build first; if the bug truly exists only in release, that calls for release-mode device runs and symbolicated stack traces instead. - **Requests outside the recorded sources.** Only `fetch`, `XMLHttpRequest` and `<Image>` are recorded in the core panel; other stacks need logpoints or Expo's own network panel. - **LogBox versus Console.** While DevTools is open, LogBox hides every error except fatal ones, so treat the Console panel as the source of truth. - **Native code.** If the failure is inside a native authentication module, DevTools shows only the JavaScript side of the call; the native half is debugged in Xcode or Android Studio.
- Why use a logpoint here rather than adding console.log to the handler?A logpoint is set in the Sources panel and prints an expression each time the line runs without touching the file, so there is no code change, no Fast Refresh cycle and nothing to clean up before committing. It suits cases where pausing would change what you're watching, such as a request racing a timeout.
- The login fails only in the release build the school tested. Can React Native DevTools help?Not directly: DevTools, the Dev Menu and LogBox are disabled in release builds. First try to reproduce in a debug build against the same backend and configuration. If it truly happens only in release, it needs release-mode runs on a device and symbolicated stack traces, not the DevTools inspector.
- While paused on a breakpoint, why does the app look frozen?The breakpoint halts the app's JavaScript in Hermes, so no React updates, JavaScript timers or event handlers run until you resume. The device shows a Paused in Debugger overlay; tapping it, or resuming in the Sources panel, lets the JavaScript continue.
saying these in an interview costs you the question
- Add console.log everywhere and read the output in the Metro terminal.
- An empty Network panel proves the phone is offline.
- fetch rejects on a 5xx, so the catch block proves a network outage.
- Attach DevTools to the release build from its Dev Menu.
- Plain Pause on exceptions will stop on an error your catch block handles.