@samitouri / QOS-React / commits / 2bbb7be0e1

[DevTools] Don't call Hooks conditionally (#34644)

Sebastian "Sebbie" Silbermann committed Sep 29, 2025 at 15:15 UTC 2bbb7be0e193e53ee625dfd496a30bcec60bebd5
2 files changed +50 -40
packages/react-devtools-shared/src/devtools/views/Profiler/SidebarEventInfo.js
+28 -18
@@ -8,6 +8,7 @@
8 */
9
10 import type {SchedulingEvent} from 'react-devtools-timeline/src/types';
11 +import type {ReactFunctionLocation} from 'shared/ReactTypes';
12
13 import * as React from 'react';
14 import Button from '../Button';
@@ -27,6 +28,28 @@ import styles from './SidebarEventInfo.css';
28
29 export type Props = {};
30
31 +type FunctionLocationProps = {
32 + location: ReactFunctionLocation,
33 + displayName: string,
34 +};
35 +function FunctionLocation({location, displayName}: FunctionLocationProps) {
36 + // TODO: We should support symbolication here as well, but
37 + // symbolicating the whole stack can be expensive
38 + const [canViewSource, viewSource] = useOpenResource(location, null);
39 + return (
40 + <li>
41 + <Button
42 + className={
43 + canViewSource ? styles.ClickableSource : styles.UnclickableSource
44 + }
45 + disabled={!canViewSource}
46 + onClick={viewSource}>
47 + {displayName}
48 + </Button>
49 + </li>
50 + );
51 +}
52 +
53 type SchedulingEventProps = {
54 eventInfo: SchedulingEvent,
55 };
@@ -74,25 +97,12 @@ function SchedulingEventInfo({eventInfo}: SchedulingEventProps) {
97 );
98 }
99
77 - // TODO: We should support symbolication here as well, but
78 - // symbolicating the whole stack can be expensive
79 - const [canViewSource, viewSource] = useOpenResource(
80 - location,
81 - null,
82 - );
100 return (
84 - <li key={index}>
85 - <Button
86 - className={
87 - canViewSource
88 - ? styles.ClickableSource
89 - : styles.UnclickableSource
90 - }
91 - disabled={!canViewSource}
92 - onClick={viewSource}>
93 - {displayName}
94 - </Button>
95 - </li>
101 + <FunctionLocation
102 + key={index}
103 + displayName={displayName}
104 + location={location}
105 + />
106 );
107 },
108 )}
packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTimeline.js
+22 -22
@@ -39,28 +39,6 @@ function SuspenseTimelineInput() {
39 const min = 0;
40 const max = timeline.length > 0 ? timeline.length - 1 : 0;
41
42 - if (rootID === null) {
43 - return (
44 - <div className={styles.SuspenseTimelineInput}>No root selected.</div>
45 - );
46 - }
47 -
48 - if (!store.supportsTogglingSuspense(rootID)) {
49 - return (
50 - <div className={styles.SuspenseTimelineInput}>
51 - Can't step through Suspense in production apps.
52 - </div>
53 - );
54 - }
55 -
56 - if (timeline.length === 0) {
57 - return (
58 - <div className={styles.SuspenseTimelineInput}>
59 - Root contains no Suspense nodes.
60 - </div>
61 - );
62 - }
63 -
42 function switchSuspenseNode(nextTimelineIndex: number) {
43 const nextSelectedSuspenseID = timeline[nextTimelineIndex];
44 highlightHostInstance(nextSelectedSuspenseID);
@@ -175,6 +153,28 @@ function SuspenseTimelineInput() {
153 };
154 }, [playing]);
155
156 + if (rootID === null) {
157 + return (
158 + <div className={styles.SuspenseTimelineInput}>No root selected.</div>
159 + );
160 + }
161 +
162 + if (!store.supportsTogglingSuspense(rootID)) {
163 + return (
164 + <div className={styles.SuspenseTimelineInput}>
165 + Can't step through Suspense in production apps.
166 + </div>
167 + );
168 + }
169 +
170 + if (timeline.length === 0) {
171 + return (
172 + <div className={styles.SuspenseTimelineInput}>
173 + Root contains no Suspense nodes.
174 + </div>
175 + );
176 + }
177 +
178 return (
179 <>
180 <Button