Claude
Skills
Sign in
Back

debug:react

Included with Lifetime
$97 forever

Debug React issues systematically. Use when encountering component errors like "Cannot read property of undefined", infinite re-render loops with "Too many re-renders", stale closures in hooks, key prop warnings, memory leaks with useEffect, hydration mismatches in SSR/SSG applications, hook rule violations, missing dependency warnings, or performance bottlenecks. Covers functional components, hooks, and modern React 18/19 patterns including Server Components and concurrent features.

Design

What this skill does


# React Debugging Guide

A systematic approach to debugging React applications, covering common error patterns, modern debugging tools, and step-by-step resolution strategies.

## Common Error Patterns

### 1. "Cannot read property of undefined" / "TypeError: X is undefined"

**Cause:** Accessing properties on null/undefined values, often from:
- Uninitialized state
- API data not yet loaded
- Incorrect prop drilling

**Solutions:**
```jsx
// Problem: Accessing nested property before data loads
const name = user.profile.name; // Error if user is undefined

// Solution 1: Optional chaining
const name = user?.profile?.name;

// Solution 2: Default values
const name = user?.profile?.name ?? 'Unknown';

// Solution 3: Early return pattern
if (!user) return <LoadingSpinner />;
return <div>{user.profile.name}</div>;

// Solution 4: Initialize state properly
const [user, setUser] = useState({ profile: { name: '' } });
```

### 2. Infinite Re-render Loops ("Too many re-renders")

**Cause:** State updates triggering renders that trigger more state updates.

**Common Triggers:**
- Calling setState directly in render
- useEffect with missing/incorrect dependencies
- Object/array references changing every render

**Solutions:**
```jsx
// Problem: setState in render
function BadComponent() {
  const [count, setCount] = useState(0);
  setCount(count + 1); // Infinite loop!
  return <div>{count}</div>;
}

// Solution: Move to useEffect or event handler
function GoodComponent() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    setCount(c => c + 1);
  }, []); // Run once on mount
  return <div>{count}</div>;
}

// Problem: Object dependency causing infinite loop
useEffect(() => {
  fetchData(options);
}, [options]); // New object reference every render!

// Solution: useMemo for stable reference
const memoizedOptions = useMemo(() => ({ page: 1 }), []);
useEffect(() => {
  fetchData(memoizedOptions);
}, [memoizedOptions]);
```

### 3. Stale Closure in Hooks

**Cause:** Callbacks capture old values from previous renders.

**Solutions:**
```jsx
// Problem: Stale closure in interval
function Timer() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      console.log(count); // Always logs initial value!
      setCount(count + 1); // Only increments once
    }, 1000);
    return () => clearInterval(id);
  }, []); // Empty deps = stale closure
}

// Solution 1: Functional update
setCount(prevCount => prevCount + 1);

// Solution 2: useRef for mutable values
const countRef = useRef(count);
useEffect(() => {
  countRef.current = count;
}, [count]);

// Solution 3: Include dependency (if appropriate)
useEffect(() => {
  const id = setInterval(() => {
    setCount(count + 1);
  }, 1000);
  return () => clearInterval(id);
}, [count]); // Re-creates interval on each count change

// Solution 4: useEffectEvent (React 19.2+)
const onTick = useEffectEvent(() => {
  setCount(count + 1); // Always has fresh count
});
useEffect(() => {
  const id = setInterval(onTick, 1000);
  return () => clearInterval(id);
}, []);
```

### 4. Key Prop Warnings

**Cause:** Missing or non-unique keys in lists.

**Solutions:**
```jsx
// Problem: No key
{items.map(item => <Item data={item} />)}

// Problem: Index as key (causes issues with reordering)
{items.map((item, index) => <Item key={index} data={item} />)}

// Solution: Stable unique identifier
{items.map(item => <Item key={item.id} data={item} />)}

// For items without IDs, generate stable keys
{items.map(item => <Item key={`${item.name}-${item.date}`} data={item} />)}
```

### 5. Memory Leaks with useEffect

**Cause:** Subscriptions, timers, or async operations not cleaned up.

**Solutions:**
```jsx
// Problem: No cleanup
useEffect(() => {
  const subscription = dataSource.subscribe(handleChange);
  // Memory leak when component unmounts!
}, []);

// Solution: Return cleanup function
useEffect(() => {
  const subscription = dataSource.subscribe(handleChange);
  return () => subscription.unsubscribe();
}, []);

// Problem: Async operation after unmount
useEffect(() => {
  fetchData().then(data => {
    setData(data); // Error if unmounted!
  });
}, []);

// Solution: AbortController for fetch
useEffect(() => {
  const controller = new AbortController();

  fetchData({ signal: controller.signal })
    .then(data => setData(data))
    .catch(err => {
      if (err.name !== 'AbortError') throw err;
    });

  return () => controller.abort();
}, []);

// Solution: Ignore flag for other async
useEffect(() => {
  let ignore = false;

  fetchData().then(data => {
    if (!ignore) setData(data);
  });

  return () => { ignore = true; };
}, []);
```

### 6. Hydration Mismatches (SSR/SSG)

**Cause:** Server-rendered HTML differs from client-side React.

**Common Triggers:**
- Using `Date.now()`, `Math.random()` in render
- Browser-only APIs (window, localStorage)
- Conditional rendering based on client state

**Solutions:**
```jsx
// Problem: Random value differs server vs client
function BadComponent() {
  return <div>{Math.random()}</div>; // Hydration mismatch!
}

// Solution 1: useEffect for client-only values
function GoodComponent() {
  const [randomValue, setRandomValue] = useState(null);

  useEffect(() => {
    setRandomValue(Math.random());
  }, []);

  return <div>{randomValue}</div>;
}

// Solution 2: suppressHydrationWarning (use sparingly)
<time suppressHydrationWarning>{new Date().toISOString()}</time>

// Solution 3: Client-only component
const ClientOnlyComponent = dynamic(
  () => import('./ClientComponent'),
  { ssr: false }
);
```

### 7. Hook Rules Violations

**Error:** "React Hook useXXX is called conditionally"

**Cause:** Hooks called inside conditions, loops, or after early returns.

**Solutions:**
```jsx
// Problem: Conditional hook
function BadComponent({ shouldFetch }) {
  if (shouldFetch) {
    useEffect(() => fetchData(), []); // Error!
  }
}

// Solution: Condition inside hook
function GoodComponent({ shouldFetch }) {
  useEffect(() => {
    if (shouldFetch) fetchData();
  }, [shouldFetch]);
}

// Problem: Hook after early return
function BadComponent({ data }) {
  if (!data) return null;
  const [state, setState] = useState(data); // Error!
}

// Solution: Move hooks before returns
function GoodComponent({ data }) {
  const [state, setState] = useState(data);
  if (!data) return null;
  return <div>{state}</div>;
}
```

### 8. Missing Dependencies Warning

**Error:** "React Hook has a missing dependency: 'XXX'"

**Solutions:**
```jsx
// Problem: Missing dependency
const [count, setCount] = useState(0);
useEffect(() => {
  document.title = `Count: ${count}`;
}, []); // Warning: missing 'count'

// Solution 1: Add the dependency
useEffect(() => {
  document.title = `Count: ${count}`;
}, [count]);

// Solution 2: Remove if truly not needed (rare)
// eslint-disable-next-line react-hooks/exhaustive-deps

// Solution 3: useCallback for function dependencies
const handleClick = useCallback(() => {
  console.log(count);
}, [count]);

useEffect(() => {
  element.addEventListener('click', handleClick);
  return () => element.removeEventListener('click', handleClick);
}, [handleClick]);
```

## Debugging Tools

### React Developer Tools

The official browser extension for debugging React applications.

**Installation:**
- Chrome: [React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools)
- Firefox: [React Developer Tools](https://addons.mozilla.org/en-US/firefox/addon/react-devtools/)

**Key Features:**
```
Components Tab:
- Inspect component tree hierarchy
- View and edit props in real-time
- View and modify state
- Search components by name
- View component source location

Profiler Tab:
- Record render performance
- Identify slow components
- View render timing flamegraph
- Detect unnecessary re-renders
```

**Pro Tips:**
```jsx
// Name components for easier debugging
const MyComponent = () => <div />;
MyComponent.displayName = 'MyC

Related in Design