React Doctor

Diagnose, debug, and fix issues in React applications to improve code quality and performance

React Doctor is a community skill for diagnosing and fixing common React application issues, covering performance problems, state bugs, rendering errors, hook violations, and dependency conflicts in React projects.

What Is This?

Overview

React Doctor provides diagnostic tools and solutions for common React application problems. It covers performance problems that identify unnecessary re-renders and memory leaks through profiling, state bugs that trace incorrect updates and stale closures in asynchronous state management, rendering errors that debug hydration mismatches and infinite render loops, hook violations that detect conditional calls and incorrect dependency arrays, and dependency conflicts that resolve version mismatches across React packages. The skill helps developers systematically diagnose React issues.

Who Should Use This

This skill serves React developers troubleshooting application bugs, teams maintaining large React codebases with recurring issues, and developers debugging performance regressions in production React applications.

Why Use It?

Problems It Solves

React applications re-render entire component subtrees when a single state value changes causing visible performance degradation. Stale closure bugs in useEffect and useCallback produce incorrect behavior that is difficult to trace without understanding hook closure semantics. Hydration mismatches between server and client rendering cause content flashing and console warnings. Memory leaks from uncleared subscriptions and timers in useEffect accumulate over time in long-running applications.

Core Highlights

Render analyzer identifies components that re-render unnecessarily. State debugger traces state update flows and detects stale closures. Hook validator checks rules of hooks compliance and dependency correctness. Error diagnostics maps common error messages to specific fixes.

How to Use It?

Basic Usage

// Diagnose re-render issues
import React, {
  useState, memo,
  useCallback,
  useRef, useEffect
} from 'react';

// Render counter hook
function useRenderCount(
  name
) {
  const count =
    useRef(0);
  count.current += 1;
  console.log(
    `${name} rendered: `
    + `${count.current}`);
  return count.current;
}

// Fix: memoize child
const ExpensiveList = memo(
  function ExpensiveList({
    items, onSelect
  }) {
    useRenderCount(
      'ExpensiveList');
    return (
      <ul>
        {items.map(item =>
          <li key={item.id}
            onClick={() =>
              onSelect(
                item.id)}>
            {item.name}
          </li>)}
      </ul>);
  });

function Parent() {
  const [count,
    setCount] =
    useState(0);
  const [items] = useState(
    [{id: 1,
      name: 'Item 1'}]);

  // Fix: stable ref
  const onSelect =
    useCallback(
      id => console.log(
        id), []);

  return (
    <div>
      <button onClick={
        () => setCount(
          c => c + 1)}>
        Count: {count}
      </button>
      <ExpensiveList
        items={items}
        onSelect={
          onSelect} />
    </div>);
}

Real-World Examples

// Fix memory leaks
import { useState,
  useEffect,
  useRef } from 'react';

// Bad: memory leak
function BadTimer() {
  const [count,
    setCount] =
    useState(0);

  useEffect(() => {
    setInterval(() =>
      setCount(c =>
        c + 1), 1000);
    // Missing cleanup
  }, []);

  return <p>{count}</p>;
}

// Fixed: proper cleanup
function GoodTimer() {
  const [count,
    setCount] =
    useState(0);

  useEffect(() => {
    const id =
      setInterval(() =>
        setCount(c =>
          c + 1), 1000);
    return () =>
      clearInterval(id);
  }, []);

  return <p>{count}</p>;
}

// Fix stale closure
function ChatRoom({
  roomId
}) {
  const [messages,
    setMessages] =
    useState([]);
  const roomRef =
    useRef(roomId);

  useEffect(() => {
    roomRef.current =
      roomId;
  }, [roomId]);

  useEffect(() => {
    const ws = new
      WebSocket(
        `/ws/${roomId}`);
    ws.onmessage = e => {
      if (roomRef.current
        === roomId)
        setMessages(prev =>
          [...prev,
            JSON.parse(
              e.data)]);
    };
    return () =>
      ws.close();
  }, [roomId]);

  return messages.map(
    (m, i) =>
      <p key={i}>
        {m.text}</p>);
}

Advanced Tips

Use React DevTools Profiler to record and inspect component render timings before applying memoization. Add the why-did-you-render library during development to automatically log unnecessary re-renders. Check useEffect dependency arrays with the exhaustive-deps ESLint rule to catch stale closure bugs early.

When to Use It?

Use Cases

Debug a React application where certain interactions feel slow by profiling component render times. Fix a memory leak caused by WebSocket connections not being closed in useEffect cleanup. Resolve hydration mismatch warnings in a server-rendered React application.

Related Topics

React, debugging, performance profiling, hooks, state management, memory leaks, and component rendering.

Important Notes

Requirements

React DevTools browser extension for profiling and component inspection. ESLint with react-hooks plugin for static hook validation rules. Browser developer tools for memory profiling and network inspection during debugging.

Usage Recommendations

Do: profile with React DevTools before optimizing to confirm which components cause performance problems. Always return cleanup functions from useEffect when subscribing to events or timers. Use functional state updates to avoid stale closure issues in asynchronous callbacks.

Don't: apply memo to every component without evidence of re-render problems since memoization itself has overhead costs. Suppress exhaustive-deps warnings with eslint-disable instead of fixing the dependency array. Ignore React StrictMode double-render warnings since they reveal real cleanup issues.

Limitations

React DevTools profiling adds overhead that may not perfectly reflect production performance characteristics. Some rendering issues only appear under specific data conditions that are difficult to reproduce in development environments. Third-party component libraries may have internal rendering patterns that cannot be optimized from the consuming application.