| name | react-debugging-advanced |
| description | Advanced React debugging techniques using browser DevTools, React DevTools, performance profiling, and error boundaries. Use for troubleshooting complex React applications and performance issues. |
| allowed-tools | fs_read fs_write execute_bash |
| metadata | {"author":"kiro-cli","version":"1.0","category":"frontend","compatibility":"Requires React DevTools browser extension, modern browser"} |
React Advanced Debugging
Instructions
1. Set up debugging environment
Install React DevTools:
npm install -g react-devtools
Configure development build:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
define: {
__DEV__: true,
},
build: {
sourcemap: true,
},
})
2. Error boundaries and error handling
Comprehensive error boundary:
import React, { Component, ErrorInfo, ReactNode } from 'react';
interface Props {
children: ReactNode;
fallback?: ReactNode;
onError?: (error: Error, errorInfo: ErrorInfo) => void;
}
interface State {
hasError: boolean;
error?: Error;
errorInfo?: ErrorInfo;
}
export class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('ErrorBoundary caught an error:', error, errorInfo);
this.logErrorToService(error, errorInfo);
this.props.onError?.(error, errorInfo);
this.setState({ errorInfo });
}
private logErrorToService(error: Error, errorInfo: ErrorInfo) {
console.log('Logging error to service:', {
message: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent,
url: window.location.href,
});
}
render() {
if (this.state.hasError) {
if (this.props.fallback) {
return this.props.fallback;
}
return (
<div className="error-boundary">
<h2>Something went wrong</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
<summary>Error Details</summary>
<p><strong>Error:</strong> {this.state.error?.message}</p>
<p><strong>Stack:</strong> {this.state.error?.stack}</p>
<p><strong>Component Stack:</strong> {this.state.errorInfo?.componentStack}</p>
</details>
<button onClick={() => window.location.reload()}>
Reload Page
</button>
</div>
);
}
return this.props.children;
}
}
3. Performance debugging with React DevTools Profiler
Performance monitoring component:
import { Profiler, ProfilerOnRenderCallback } from 'react';
const onRenderCallback: ProfilerOnRenderCallback = (
id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime,
interactions
) => {
console.log('Profiler:', {
id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime,
interactions: Array.from(interactions),
});
if (actualDuration > 16) {
console.warn(`Slow render detected in ${id}: ${actualDuration}ms`);
}
};
export const PerformanceProfiler: React.FC<{ children: React.ReactNode; id: string }> = ({
children,
id
}) => {
return (
<Profiler id={id} onRender={onRenderCallback}>
{children}
</Profiler>
);
};
function App() {
return (
<PerformanceProfiler id="App">
<Header />
<PerformanceProfiler id="MainContent">
<MainContent />
</PerformanceProfiler>
<Footer />
</PerformanceProfiler>
);
}
4. Custom debugging hooks
Debug hook for component lifecycle:
import { useEffect, useRef } from 'react';
export function useDebugValue(value: any, label: string = 'Debug') {
const prevValue = useRef(value);
useEffect(() => {
if (prevValue.current !== value) {
console.log(`[${label}] Changed:`, {
from: prevValue.current,
to: value,
timestamp: new Date().toISOString(),
});
prevValue.current = value;
}
});
React.useDebugValue(value, (val) => `${label}: ${JSON.stringify(val)}`);
}
export function useRenderCount(componentName: string) {
const renderCount = useRef(0);
useEffect(() => {
renderCount.current += 1;
console.log(`[${componentName}] Render #${renderCount.current}`);
});
React.useDebugValue(renderCount.current, (count) => `Renders: ${count}`);
return renderCount.current;
}
export function useWhyDidYouUpdate(name: string, props: Record<string, any>) {
const previousProps = useRef<Record<string, any>>();
useEffect(() => {
if (previousProps.current) {
const allKeys = Object.keys({ ...previousProps.current, ...props });
const changedProps: Record<string, { from: any; to: any }> = {};
allKeys.forEach(key => {
if (previousProps.current![key] !== props[key]) {
changedProps[key] = {
from: previousProps.current![key],
to: props[key],
};
}
});
if (Object.keys(changedProps).length) {
console.log(`[${name}] Props changed:`, changedProps);
}
}
previousProps.current = props;
});
}
function MyComponent({ userId, data, onUpdate }: Props) {
useRenderCount('MyComponent');
useDebugValue(userId, 'Current User ID');
useWhyDidYouUpdate('MyComponent', { userId, data, onUpdate });
}
5. State debugging with Redux DevTools
Enhanced store configuration:
import { configureStore } from '@reduxjs/toolkit';
import { createLogger } from 'redux-logger';
const logger = createLogger({
predicate: () => process.env.NODE_ENV === 'development',
collapsed: true,
duration: true,
timestamp: true,
logErrors: true,
diff: true,
});
export const store = configureStore({
reducer: {
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: ['persist/PERSIST'],
},
}).concat(logger),
devTools: process.env.NODE_ENV === 'development' && {
trace: true,
traceLimit: 25,
actionSanitizer: (action) => ({
...action,
payload: action.type.includes('password') ? '[REDACTED]' : action.payload,
}),
stateSanitizer: (state) => ({
...state,
auth: state.auth ? { ...state.auth, token: '[REDACTED]' } : state.auth,
}),
},
});
6. Network debugging and API monitoring
API debugging wrapper:
import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
const apiClient = axios.create({
baseURL: process.env.REACT_APP_API_URL,
});
apiClient.interceptors.request.use(
(config: AxiosRequestConfig) => {
const requestId = Math.random().toString(36).substr(2, 9);
config.metadata = { requestId, startTime: Date.now() };
console.log(`[API Request ${requestId}]`, {
method: config.method?.toUpperCase(),
url: config.url,
params: config.params,
data: config.data,
headers: config.headers,
});
return config;
},
(error: AxiosError) => {
console.error('[API Request Error]', error);
return Promise.reject(error);
}
);
apiClient.interceptors.response.use(
(response: AxiosResponse) => {
const { requestId, startTime } = response.config.metadata || {};
const duration = Date.now() - (startTime || 0);
console.log(`[API Response ${requestId}] ${duration}ms`, {
status: response.status,
statusText: response.statusText,
data: response.data,
headers: response.headers,
});
if (duration > 1000) {
console.warn(`[API] Slow request detected: ${duration}ms for ${response.config.url}`);
}
return response;
},
(error: AxiosError) => {
const { requestId, startTime } = error.config?.metadata || {};
const duration = Date.now() - (startTime || 0);
console.error(`[API Error ${requestId}] ${duration}ms`, {
message: error.message,
status: error.response?.status,
statusText: error.response?.statusText,
data: error.response?.data,
config: {
method: error.config?.method,
url: error.config?.url,
params: error.config?.params,
},
});
return Promise.reject(error);
}
);
7. Memory leak detection
Memory monitoring hook:
import { useEffect, useRef } from 'react';
export function useMemoryMonitor(componentName: string, interval: number = 5000) {
const intervalRef = useRef<NodeJS.Timeout>();
useEffect(() => {
if (process.env.NODE_ENV === 'development' && 'memory' in performance) {
intervalRef.current = setInterval(() => {
const memory = (performance as any).memory;
console.log(`[Memory ${componentName}]`, {
used: `${Math.round(memory.usedJSHeapSize / 1048576)} MB`,
total: `${Math.round(memory.totalJSHeapSize / 1048576)} MB`,
limit: `${Math.round(memory.jsHeapSizeLimit / 1048576)} MB`,
timestamp: new Date().toISOString(),
});
}, interval);
}
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, [componentName, interval]);
}
export function useLeakDetection(componentName: string) {
const mountTime = useRef(Date.now());
const cleanupFunctions = useRef<(() => void)[]>([]);
const addCleanup = (fn: () => void) => {
cleanupFunctions.current.push(fn);
};
useEffect(() => {
return () => {
const lifetime = Date.now() - mountTime.current;
console.log(`[${componentName}] Unmounted after ${lifetime}ms`);
cleanupFunctions.current.forEach(fn => {
try {
fn();
} catch (error) {
console.error(`[${componentName}] Cleanup error:`, error);
}
});
};
}, [componentName]);
return { addCleanup };
}
Examples
Complete debugging setup
import React from 'react';
import { ErrorBoundary } from './components/ErrorBoundary';
import { PerformanceProfiler } from './components/PerformanceProfiler';
function App() {
return (
<ErrorBoundary
onError={(error, errorInfo) => {
// Send to error tracking service
console.error('App Error:', error, errorInfo);
}}
>
<PerformanceProfiler id="App">
<div className="app">
<Header />
<MainContent />
<Footer />
</div>
</PerformanceProfiler>
</ErrorBoundary>
);
}
export default App;
Debug component with all hooks
function DebugComponent({ userId, data }: Props) {
useRenderCount('DebugComponent');
useMemoryMonitor('DebugComponent');
useDebugValue(userId, 'User ID');
useWhyDidYouUpdate('DebugComponent', { userId, data });
const { addCleanup } = useLeakDetection('DebugComponent');
useEffect(() => {
const timer = setInterval(() => {
console.log('Timer tick');
}, 1000);
addCleanup(() => clearInterval(timer));
}, [addCleanup]);
return <div>Debug Component Content</div>;
}
Troubleshooting
- React DevTools not showing: Ensure development build and extension installed
- Performance profiler not recording: Check if Profiler component wraps target components
- Error boundary not catching errors: Async errors need separate handling
- Memory leaks detected: Check for uncleared intervals, event listeners, subscriptions
- API calls not logged: Verify interceptors are properly configured
- State changes not visible: Ensure Redux DevTools extension is installed and enabled