Skip to the solution
EasyFrontend7 min read

How do you debug React applications?

358 views

The problem

Question presented to candidate: "A bug report says a page shows stale data and feels sluggish. Walk me through how you would debug that in a React app."

What a strong answer should cover:

  • A method, not a tool list: reproduce, narrow to a component, then inspect the data flowing into it.
  • React DevTools Components for wrong values β€” props, state, hooks, context, and the owner chain that answers "where did this come from?".
  • React DevTools Profiler for slow interactions, with "record why each component rendered" enabled.
  • StrictMode as a proactive detector of impure renders and missing effect cleanup, not a nuisance.
  • Error Boundaries to catch render-phase crashes and show a real fallback plus a component stack.
  • Knowing what an Error Boundary does not catch: event handlers, async code, SSR, and errors in the boundary itself.
  • React 19 additions: captureOwnerStack for the chain of components that created an element.
  • Ordinary JavaScript debugging still applies β€” breakpoints, conditional breakpoints, the network panel.
  • The common root causes worth naming: stale closures, missing effect dependencies, and unstable object or function identities.

Clarifying questions expected:

  • "Does it reproduce in development, or only in production?" β€” that changes the toolkit entirely.
  • "Is it wrong data, a crash, or slowness?" β€” the three lead to different tools.

Code / implementation expected: Optional. An Error Boundary is the one piece of code worth being able to write from memory.

  • debugging
  • dev tools
  • error handling
  • strictmode

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

7 min read Β· 10 sections

Try it in the playground

Open any example in the playground to run and change it.

ReactAn Error Boundary, and the errors it deliberately cannot catch
import { Component, useState } from "react";

// Error Boundaries still require a class β€” there is no hook equivalent.
class ErrorBoundary extends Component {
  state = { error: null };

  // Render the fallback on the next render after a child throws.
  static getDerivedStateFromError(error) {
    return { error };
  }

  // Side effects belong here: log to Sentry, etc. info.componentStack tells
  // you which component threw, which a plain JS stack trace will not.
  componentDidCatch(error, info) {
    console.log("[boundary] caught:", error.message);
    console.log("[boundary] component stack:", info.componentStack);
  }

  render() {
    if (this.state.error) {
      return (
        <div style={{ padding: 12, border: "1px solid crimson", borderRadius: 6, color: "crimson" }}>
          <strong>Something went wrong:</strong> {this.state.error.message}{" "}
          <button onClick={() => this.setState({ error: null })}>Retry</button>
        </div>
      );
    }
    return this.props.children;
  }
}

// Throws during RENDER β€” the boundary catches this.
function ExplodesOnRender({ boom }) {
  if (boom) throw new Error("render-phase error");
  return <p>Rendering fine.</p>;
}

// Throws inside an EVENT HANDLER β€” the boundary does NOT catch this. It is
// ordinary JavaScript and needs try/catch.
function ExplodesOnClick() {
  const [caught, setCaught] = useState(null);
  return (
    <p>
      <button onClick={() => { throw new Error("handler error β€” boundary will NOT catch this"); }}>
        Throw in a handler (uncaught)
      </button>{" "}
      <button
        onClick={() => {
          try { throw new Error("handler error"); }
          catch (e) { setCaught(e.message); }
        }}
      >
        Throw, but with try/catch
      </button>
      {caught && <em style={{ color: "green" }}> handled: {caught}</em>}
    </p>
  );
}

export default function App() {
  const [boom, setBoom] = useState(false);

  return (
    <div style={{ padding: 24, fontFamily: "system-ui", lineHeight: 1.6 }}>
      <h3>Caught by the boundary</h3>
      <ErrorBoundary>
        <ExplodesOnRender boom={boom} />
      </ErrorBoundary>
      <button onClick={() => setBoom(true)}>Trigger a render error</button>

      <h3>Not caught by the boundary</h3>
      <ErrorBoundary>
        <ExplodesOnClick />
      </ErrorBoundary>

      <p style={{ color: "#666", fontSize: 13 }}>
        The first button is caught and shows a fallback. The first button in the
        second group escapes to the console β€” Error Boundaries cover the render,
        lifecycle and constructor phases only, never event handlers or async code.
      </p>
    </div>
  );
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 19 of 119 in React.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All React.js questions
Up nextEasy

What is the purpose of `React.StrictMode`?

Continue