Skip to content

Commit 503337f

Browse files
committed
docs: enhance React Developer Tools with comprehensive debugging guide
1 parent e901790 commit 503337f

File tree

1 file changed

+312
-12
lines changed

1 file changed

+312
-12
lines changed

src/content/learn/react-developer-tools.md

Lines changed: 312 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,58 +4,358 @@ title: React Developer Tools
44

55
<Intro>
66

7-
Use React Developer Tools to inspect React [components](/learn/your-first-component), edit [props](/learn/passing-props-to-a-component) and [state](/learn/state-a-components-memory), and identify performance problems.
7+
React Developer Tools is a powerful browser extension that helps you inspect React components, edit props and state, and identify performance issues. This guide provides practical, step-by-step examples to debug common React challenges, making it easier to build robust applications.
88

99
</Intro>
1010

1111
<YouWillLearn>
1212

1313
* How to install React Developer Tools
14+
* How to debug component issues step-by-step
15+
* How to identify and fix performance bottlenecks
16+
* How to debug state updates and effects
17+
* How to track down error sources effectively
1418

1519
</YouWillLearn>
1620

17-
## Browser extension {/*browser-extension*/}
21+
## Browser Extension {/*browser-extension*/}
1822

19-
The easiest way to debug websites built with React is to install the React Developer Tools browser extension. It is available for several popular browsers:
23+
The easiest way to debug React websites is to install the React Developer Tools browser extension, available for:
2024

2125
* [Install for **Chrome**](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en)
2226
* [Install for **Firefox**](https://addons.mozilla.org/en-US/firefox/addon/react-devtools/)
2327
* [Install for **Edge**](https://microsoftedge.microsoft.com/addons/detail/react-developer-tools/gpphkfbcpidddadnkolkpfckpihlkkil)
2428

25-
Now, if you visit a website **built with React,** you will see the _Components_ and _Profiler_ panels.
29+
When you visit a React-built website, you'll see the _Components_ and _Profiler_ tabs in your browser's developer tools.
2630

2731
![React Developer Tools extension](/images/docs/react-devtools-extension.png)
2832

29-
### Safari and other browsers {/*safari-and-other-browsers*/}
30-
For other browsers (for example, Safari), install the [`react-devtools`](https://www.npmjs.com/package/react-devtools) npm package:
33+
## Safari and Other Browsers {/*safari-and-other-browsers*/}
34+
35+
For browsers like Safari, install the [`react-devtools`](https://www.npmjs.com/package/react-devtools) npm package:
36+
3137
```bash
3238
# Yarn
3339
yarn global add react-devtools
3440

35-
# Npm
41+
# npm
3642
npm install -g react-devtools
3743
```
3844

39-
Next open the developer tools from the terminal:
45+
Open the developer tools from the terminal:
46+
4047
```bash
4148
react-devtools
4249
```
4350

44-
Then connect your website by adding the following `<script>` tag to the beginning of your website's `<head>`:
51+
Add this `<script>` tag to your website's `<head>`:
52+
4553
```html {3}
4654
<html>
4755
<head>
4856
<script src="http://localhost:8097"></script>
4957
```
5058

51-
Reload your website in the browser now to view it in developer tools.
59+
Reload your website to view it in the developer tools.
5260

5361
![React Developer Tools standalone](/images/docs/react-devtools-standalone.png)
5462

5563
## Mobile (React Native) {/*mobile-react-native*/}
5664

57-
To inspect apps built with [React Native](https://reactnative.dev/), you can use [React Native DevTools](https://reactnative.dev/docs/react-native-devtools), the built-in debugger that deeply integrates React Developer Tools. All features work identically to the browser extension, including native element highlighting and selection.
65+
For React Native apps, use [React Native DevTools](https://reactnative.dev/docs/react-native-devtools), which integrates React Developer Tools for native element highlighting and debugging. Features mirror the browser extension.
5866

5967
[Learn more about debugging in React Native.](https://reactnative.dev/docs/debugging)
6068

61-
> For versions of React Native earlier than 0.76, please use the standalone build of React DevTools by following the [Safari and other browsers](#safari-and-other-browsers) guide above.
69+
> For React Native versions before 0.76, follow the [Safari and other browsers](#safari-and-other-browsers) guide.
70+
71+
## Common Debugging Scenarios {/*common-debugging-scenarios*/}
72+
73+
React Developer Tools is most effective when used systematically. Below are practical examples to debug common issues, with step-by-step instructions and code snippets.
74+
75+
### Debugging Unexpected Re-Renders {/*debugging-unexpected-re-renders*/}
76+
77+
**Scenario**: Your app feels sluggish due to components re-rendering unnecessarily, impacting performance.
78+
79+
**Steps to Debug:**
80+
81+
1. Open React Developer Tools and select the **Profiler** tab.
82+
2. Click the record button (🔴) to start profiling.
83+
3. Interact with your app (e.g., click buttons, type in inputs) to trigger re-renders.
84+
4. Stop recording and review the flame graph, where wider bars indicate longer render times and multiple bars show frequent re-renders.
85+
5. Click a component to see the "Why did this render?" section, identifying causes like prop or state changes.
86+
6. Check if parent components are causing unnecessary child re-renders.
87+
88+
**Common Causes:**
89+
90+
* New object/array references created on every render.
91+
* Missing memoization for props or functions.
92+
* Unnecessary state updates.
93+
94+
**Solution:**
95+
Consider this example where a child component re-renders unnecessarily:
96+
97+
```jsx
98+
function Parent() {
99+
const [count, setCount] = useState(0);
100+
const data = { value: 'test' }; // New object every render
101+
return (
102+
<div>
103+
<button onClick={() => setCount(count + 1)}>Increment</button>
104+
<Child data={data} />
105+
</div>
106+
);
107+
}
108+
109+
function Child({ data }) {
110+
return <div>{data.value}</div>;
111+
}
112+
```
113+
114+
Clicking "Increment" causes Parent to re-render, creating a new data object, which triggers Child to re-render.
115+
116+
**Fix:**
117+
Use `useMemo` to stabilize the data reference:
118+
119+
```jsx
120+
function Parent() {
121+
const [count, setCount] = useState(0);
122+
const data = useMemo(() => ({ value: 'test' }), []); // Stable reference
123+
return (
124+
<div>
125+
<button onClick={() => setCount(count + 1)}>Increment</button>
126+
<Child data={data} />
127+
</div>
128+
);
129+
}
130+
```
131+
132+
Now, Child only re-renders if data changes. Use the Profiler to confirm reduced render counts.
133+
134+
### Debugging State Updates That Don't Work {/*debugging-state-updates*/}
135+
136+
**Scenario**: Clicking a button doesn't update the UI as expected, despite state changes.
137+
138+
**Steps to Debug:**
139+
140+
1. Open the **Components** tab in React Developer Tools.
141+
2. Select the component in the tree and inspect its state values.
142+
3. Trigger the action (e.g., button click) and watch if state updates in real-time.
143+
4. If state doesn't change, check the event handler logic.
144+
5. If state changes but the UI doesn't update, verify JSX rendering logic.
145+
146+
**Common Causes:**
147+
148+
* Mutating state directly instead of using setState.
149+
* Incorrect state dependencies or conditional rendering.
150+
151+
**Solution:**
152+
Consider this faulty component:
153+
154+
```jsx
155+
function Counter() {
156+
const [count, setCount] = useState(0);
157+
const increment = () => {
158+
count += 1; // Incorrect: mutates state directly
159+
};
160+
return (
161+
<div>
162+
<p>Count: {count}</p>
163+
<button onClick={increment}>Increment</button>
164+
</div>
165+
);
166+
}
167+
```
168+
169+
The UI doesn't update because count is mutated directly.
170+
171+
**Fix:**
172+
Use `setCount`:
173+
174+
```jsx
175+
const increment = () => {
176+
setCount(prevCount => prevCount + 1); // Correct: triggers re-render
177+
};
178+
```
179+
180+
Use DevTools to confirm count updates in the Components tab.
181+
182+
### Debugging useEffect Issues {/*debugging-useeffect-issues*/}
183+
184+
**Scenario**: An effect runs too often or not at all, causing performance issues or missing updates.
185+
186+
**Steps to Debug:**
187+
188+
1. Add a `console.log` inside your `useEffect` to track when it runs:
189+
190+
```jsx
191+
useEffect(() => {
192+
console.log('Effect ran with:', { dep1, dep2 });
193+
}, [dep1, dep2]);
194+
```
195+
196+
2. Open the **Components** tab and select the component.
197+
3. Inspect the hooks section to view dependency values.
198+
4. Check if dependencies are stable or changing unnecessarily.
199+
200+
**Common Causes:**
201+
202+
* Missing dependencies in the array, causing stale data.
203+
* Unstable references (e.g., objects created on every render).
204+
205+
**Solution:**
206+
Consider this problematic effect:
207+
208+
```jsx
209+
function UserProfile({ userId }) {
210+
const [user, setUser] = useState(null);
211+
useEffect(() => {
212+
fetchUser(userId).then(setUser);
213+
}, []); // Missing userId dependency
214+
}
215+
```
216+
217+
The effect doesn't run when userId changes, leading to outdated data.
218+
219+
**Fix:**
220+
Add userId to the dependency array:
221+
222+
```jsx
223+
useEffect(() => {
224+
fetchUser(userId).then(setUser);
225+
}, [userId]);
226+
```
227+
228+
Use DevTools to verify the effect runs only when userId changes.
229+
230+
### Debugging Component Errors {/*debugging-component-errors*/}
231+
232+
**Scenario**: A component throws an error, causing the app to crash or display incorrect data.
233+
234+
**Steps to Debug:**
235+
236+
1. Wrap components with an Error Boundary to catch errors:
237+
238+
```jsx
239+
class ErrorBoundary extends Component {
240+
state = { hasError: false, error: null };
241+
static getDerivedStateFromError(error) {
242+
return { hasError: true, error };
243+
}
244+
componentDidCatch(error, errorInfo) {
245+
console.log('Error:', error, errorInfo);
246+
}
247+
render() {
248+
if (this.state.hasError) {
249+
return <h1>Error: {this.state.error?.message}</h1>;
250+
}
251+
return this.props.children;
252+
}
253+
}
254+
```
255+
256+
2. Open the **Components** tab and select the failing component.
257+
3. Check props and state for null/undefined values or incorrect types.
258+
4. Use the browser console's stack trace to trace the error source.
259+
260+
**Example:**
261+
This component crashes if user is null:
262+
263+
```jsx
264+
function UserProfile({ user }) {
265+
return <div>{user.name}</div>;
266+
}
267+
```
268+
269+
**Fix:**
270+
Add a null check:
271+
272+
```jsx
273+
function UserProfile({ user }) {
274+
if (!user) return <div>Loading...</div>;
275+
return <div>{user.name}</div>;
276+
}
277+
```
278+
279+
DevTools highlights the error in the component tree, helping you pinpoint the issue.
280+
281+
## Performance Debugging {/*performance-debugging*/}
282+
283+
The Profiler tab is key for identifying performance bottlenecks.
284+
285+
### Reading the Flame Graph {/*reading-the-flame-graph*/}
286+
287+
* **Height**: Indicates component nesting depth.
288+
* **Width**: Shows render time (wider = slower).
289+
* **Color Intensity**: Darker colors mean slower renders.
290+
* **Gray Components**: Didn't re-render (optimal).
291+
292+
### Common Performance Issues {/*common-performance-issues*/}
293+
294+
**Scenario**: Scrolling a large list is slow.
295+
296+
**Steps to Debug:**
297+
298+
1. Start profiling in the Profiler tab.
299+
2. Scroll through the list to reproduce the issue.
300+
3. Stop profiling and analyze the flame graph for slow components.
301+
4. Check for large lists or expensive calculations.
302+
303+
**Example:**
304+
This renders all items, causing lag:
305+
306+
```jsx
307+
function ProductList({ products }) {
308+
return (
309+
<div>
310+
{products.map(product => <ProductCard key={product.id} product={product} />)}
311+
</div>
312+
);
313+
}
314+
```
315+
316+
**Solution:**
317+
Use react-window for virtualization:
318+
319+
```jsx
320+
import { FixedSizeList as List } from 'react-window';
321+
322+
function ProductList({ products }) {
323+
return (
324+
<List height={600} itemCount={products.length} itemSize={120}>
325+
{({ index, style }) => (
326+
<div style={style}>
327+
<ProductCard product={products[index]} />
328+
</div>
329+
)}
330+
</List>
331+
);
332+
}
333+
```
334+
335+
Install react-window:
336+
337+
```bash
338+
npm install react-window
339+
```
340+
341+
The Profiler confirms reduced render times for visible items only.
342+
343+
## Advanced Debugging Techniques {/*advanced-debugging-techniques*/}
344+
345+
* **Console API**: Select a component in DevTools, then type `$r` in the console to inspect props (`$r.props`) or state (`$r.state` for class components).
346+
* **Select in Editor**: Right-click a component in DevTools to jump to its source code in your IDE (if supported).
347+
* **Time Travel**: Use DevTools to revert state changes and see how the UI responds, useful for debugging state transitions.
348+
349+
## Troubleshooting DevTools Issues {/*troubleshooting-devtools-issues*/}
350+
351+
* **DevTools Not Appearing**: Ensure React 16.0+ is used, check for multiple React versions (`npm ls react`), and verify development mode.
352+
* **Poor Performance**: Disable profiling when not needed, collapse large component trees, or filter components using the search box.
353+
* **Missing Component Names**: Use named components or set displayName:
354+
355+
```jsx
356+
const HelloComponent = () => <div>Hello</div>;
357+
HelloComponent.displayName = 'HelloComponent';
358+
export default HelloComponent;
359+
```
360+
361+
This enhanced documentation equips you with practical tools to debug and optimize React applications effectively.

0 commit comments

Comments
 (0)